Xposed学习--工作原理

Xposed工作原理

zygote进程

Android运行的心脏。每一个应用都是这个进程的fork,都从这个进程的一个fork开始。

Android系统启动时,zygote进程被/init.rc脚本启动。当/system/bin/app_process加载完成时,zygote启动完成。/system/bin/app_process负责加载必要的类和调用初始化方法。

Xposed

安装xposed后,会复制一份扩展后的app_process到/system/bin,这个app_process会添加一个额外的jar到classpath并且在特定的地方调用方法。

举个例子,当虚拟机创建后,在zygote的main函数执行之前,或者main函数中,xposed时zygote的一部分并且可以控制上下文。

jar位于/data/data/de.robv.android.xposed.installer/bin/XposedBridge.jar

使用Xposed可以hook方法,在方法前后注入代码。

XposedBridge还有native方法hookMethodNative,会将某个方法改成native并且把方法link到自己的native方法。

Xposed入门示例

  1. 新建Android Studio工程,选择空白activity

  2. 下载xposedAPI,放在工程的libs目录下

  3. 改动build.gradle(:app),如果有implementation fileTree()就换成compileOnly,如果没有就添加一行compileOnly

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    apply plugin: 'com.android.application'

    android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"

    defaultConfig {
    applicationId "com.example.redclocknew"
    minSdkVersion 20
    targetSdkVersion 30
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
    release {
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    }
    }

    dependencies {
    // implementation fileTree(dir: "libs", include: ["*.jar"])
    compileOnly fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    }
  4. 编辑AndroidManifest.xml,添加一系列meta-data,比如我的是这样

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.redclocknew">

    <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme" >
    <meta-data
    android:name="xposedmodule"
    android:value="true" />
    <meta-data
    android:name="xposeddescription"
    android:value="Easy example which makes the status bar clock red and adds a smiley" />
    <meta-data
    android:name="xposedminversion"
    android:value="53" />
    </application>

    </manifest>
  5. 创建类,添加代码,示例如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package com.example.redclocknew;

    import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
    import android.graphics.Color;
    import android.widget.TextView;
    import de.robv.android.xposed.IXposedHookLoadPackage;
    import de.robv.android.xposed.XC_MethodHook;
    import de.robv.android.xposed.XposedBridge;
    import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

    public class RedClock implements IXposedHookLoadPackage {
    public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
    if (!lpparam.packageName.equals("com.android.systemui"))
    return;
    // XposedBridge.log("Loaded app: " + lpparam.packageName);
    findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {

    TextView tv = (TextView) param.thisObject;
    String text = tv.getText().toString();
    tv.setText(text + "🍖");
    tv.setTextColor(Color.RED);
    if(text!=null&&text.length()>0){
    char c = text.charAt(text.length()-1);
    if((c&1)!=0){
    tv.setText(text+"🍭");
    tv.setTextColor(Color.GREEN);
    }
    }
    }
    });
    }
    }
  6. 添加assets/xposed_init文本文件,在文件中添加你的完整类名,比如我的是

    1
    com.example.redclocknew.RedClock
  7. 工具栏build->build apk(s)

  8. 找到对应的apk,使用

    1
    adb install 文件名

    进行安装

  9. 重启手机,愉快体验

自定义Xposed模块

根据Xposed实现原理,大概的替换思路有了:找到要hook的方法,然后利用xposedhook该方法并且实施自己要做的动作。

找源码有两种方式,反编译和阅读源码(如果有)

相关资料

  1. apktool

  2. jd-gui or luyten

  3. dex2java

  4. 下载android源码 or here

  5. 在线查看android源码

  6. 获取系统中指定App安装包的方法

    1. 获取栈顶activity(当前界面所属activity)

      1
      2
      3
      adb shell dumpsys window | findstr mCurrentFocus
      # or
      adb shell dumpsys activity | findstr mFucusedActivity
    2. 获取app文件安装路径

      1
      adb shell pm path com.test
      • /system/app rom附带软件
      • /system/priv-app 手机厂商定制软件
      • /data/app 用户安装软件
    3. 使用pull命令获取应用

      1
      adb pull path_to_apk path_to_store

      使用push命令可以存储应用

      1
      adb push path_to_file path_on_phone

找到可以修改的点和相关函数,注意xposed只能hook函数。

使用下面的代码确定相关app是否存在

1
2
3
4
5
6
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.android.systemui"))
return;

XposedBridge.log("we are in SystemUI!");
}

使用findAndHook方法hook方法→_→

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package de.robv.android.xposed.mods.tutorial;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

public class Tutorial implements IXposedHookLoadPackage {
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.android.systemui"))
return;

findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
}
});
}
}

参考资料

[1] Xposed turtorial

[2] Android系统启动流程之Zygote启动

[3] adb获取手机中已安装app的按转包

平方求幂&damCTF tracing-into-the-night wp

平方求幂算法

平方求幂算法是一种快速计算一个数的幂次的方法
$$
x^n=\begin{cases}
x^{\frac{n}{2}}\ if\ x\ is\ odd\
x\times x^{\frac{n-1}{2}}\ if \ x\ is\ even
\end{cases}
$$
利用这个规律可以递归求一个数的整数幂次。普通求幂复杂度为$O(n)$,该算法复杂度为$O(log_2n)$

tracing-into-the-night wp

使用ida静态分析,可以看到一些函数无法识别,但是看到了长长的十进制字符串,猜测是gnump库。进入gdb调试,有函数名,标注进IDA,得到如下反编译代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rbp
__int64 *v4; // rsi
__int64 v5; // rdx
int v6; // ST0C_4
unsigned __int64 v7; // rdi
int result; // eax
unsigned __int64 v9; // rsi
unsigned __int64 v10; // rt1
int i; // [rsp-658h] [rbp-658h]
signed int j; // [rsp-654h] [rbp-654h]
int k; // [rsp-650h] [rbp-650h]
__int64 *input_num; // [rsp-648h] [rbp-648h]
__int64 *v15; // [rsp-640h] [rbp-640h]
__int64 mpz_n; // [rsp-638h] [rbp-638h]
__int64 mpz_t1; // [rsp-628h] [rbp-628h]
mpz_t mpz_t_input; // [rsp-618h] [rbp-618h]
__int64 mpz_res; // [rsp-608h] [rbp-608h]
__int64 *v20; // [rsp-600h] [rbp-600h]
__int64 input; // [rsp-5F8h] [rbp-5F8h]
unsigned __int64 v22; // [rsp-10h] [rbp-10h]
__int64 v23; // [rsp-8h] [rbp-8h]

__asm { endbr64 }
v23 = v3;
v22 = __readfsqword(0x28u);
puts(">>> ", argv, envp);
scanf("%1499s", &input);
gmpz_inits(&mpz_n, &mpz_t1, &mpz_t_input, 0LL);
gmpz_set_str(
&mpz_n,
"44718451071997505459737203382097036763157674563410285094015196661873160587515660365739479080754310646285927975698840"
"75919491076155228633904614970948895838322855053680110252775015931685739923032704890382246914424506867596898917291406"
"11332152177145996284158921291450051409647476573907180263447394788228251424280628988395605571911678859197411847278057"
"91400587658248775006672823048997257062183136366283575435863850765350233224739628379263972613356404602105716389643245"
"17304756914352778058093193795607623318712943463163160677253581696533782339191585362014857109889012022353032581293578"
"60206058478283916330455251797686976821594656048049233954144004025483722105654884172390572538592913614550964028742160"
"90293906545836344587821140092248468913058755753661122747418378547696731251743370151912439465855419692768832807886171"
"61119953930555532581063235725600001511098008638586065561150355649771521595492214987293575278242808126982207431794687"
"22094175413271548368111459152019209395355285403167321317054983346720908955526763883023592954877344827198138450778799"
"84586727200403718241500034435659943656658321479270769282796202188603446386026445427003762433292713575110502402853472"
"3013339718674741591919194067341852869368415679054292752733683732089731387",
10LL);
gmpz_set_str(
&mpz_t1,
"20619486314897341844318499801192377793444872681514265976447810269422099657211923575185395447621708419343082909350420"
"44722984884526242093626873391758965963625822281049298074982301079998961895199836931743163026038825656545514475458669"
"01593208120708529551321959949630329565423658895459815612132400107388877020500657132548424936286415877973588244076409"
"66168118534946988780352124447250486391602877115374749255917590522107130383257354161066774381783854733236051601578721"
"00921293042212211836183729677392992987773635562510585202428914012812566497860201863986256052255479138196297630309271"
"16797845975035799543683839219783896048175102837544478413783104917473648240255777477126155618892207667435904754185970"
"34124241987364543256876480363180951673554712287184795885954238526134182476977957020075710207967797122138761794542028"
"38904288156820819377922807485116093665788839281617306977143707941755642552793152090536335517622717864224098377424237"
"71079598091135349027890359740015253411564713414564272128678631947183569729406893947313967249940381200018159756340255"
"86570301765537327984403658958087369263542596635695098080079490219110960567217248626806197822943112103408691817283370"
"6720523324225354562780368660709612457907279718303360035550622133576978589",
10LL);
gmpz_set_str(&mpz_t_input, &input, 10LL);
v4 = (__int64 *)1;
gmpz_init_set_ui(&mpz_res, 1LL);
input_num = mpz_t_input.data;
for ( i = HIDWORD(mpz_t_input.len) - 1; i >= 0; --i )
{
for ( j = 0; j <= 63; ++j )
{
gmpz_mul(&mpz_res, &mpz_res, &mpz_res); // r=r**2
v4 = &mpz_res;
gmpz_mod(&mpz_res, &mpz_res, &mpz_n); // r=r mod n
if ( (input_num[i] & 0x8000000000000000LL) == 0x8000000000000000LL )// 取最高位
{
gmpz_mul(&mpz_res, &mpz_res, &mpz_t1);
v4 = &mpz_res;
gmpz_mod(&mpz_res, &mpz_res, &mpz_n);
}
v5 = 2 * input_num[i]; // 左移1位
input_num[i] = v5;
}
}
v15 = v20;
v6 = 8 * HIDWORD(mpz_res);
v7 = (unsigned __int64)"<<< ";
puts("<<< ", v4, v5);
for ( k = v6 - 1; k >= 0; --k )
{
v7 = (unsigned int)*((char *)v15 + k);
put_char(v7);
}
result = 0;
v10 = __readfsqword(0x28u);
v9 = v10 ^ v22;
if ( v10 != v22 )
result = sub_4010E0(v7, v9);
return result;
}

可以看到是一个平方求幂算法,这里算的是
$$
res=t1^{input}mod\ n
$$
是一个rsa算法,所以找到对应的解密密钥即可。

查看trace.asm文件,这里记录了程序执行过的指令。汇编中这两句值得注意。

1
2
3
4
5
6
7
8
9
4013a6:       48 8b 00                mov    (%rax),%rax
4013a9: 48 ba 00 00 00 00 00 movabs $0x8000000000000000,%rdx
4013b3: 48 21 c2 and %rax,%rdx
4013b6: 48 b8 00 00 00 00 00 movabs $0x8000000000000000,%rax
4013c0: 48 39 c2 cmp %rax,%rdx
4013c3: 75 40 jne 401405 <main+0x1cf>
401405: 8b 85 b0 f9 ff ff mov -0x650(%rbp),%eax
40140b: 48 98 cltq
40140d: 48 8d 14 c5 00 00 00 lea 0x0(,%rax,8),%rdx

即判断是否为相等,不相等就跳转,对应程序中的if语句。使用这个特征,根据jnz语句后面的语句地址,可以判断密钥的当前位是1还是0,使用python脚本解密。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
import hashlib
flag = ''
with open('trace.asm','r') as f:
line = f.readline()
nline = ''
while line:
if line[:7]=='4013c3:':
nline = f.readline()
if not nline:
break
if nline[:7]=='401405:':
flag+='0'
else:
flag+='1'
line = f.readline()
print(flag)
print(int(flag,base=2))
1
2
0011101111111000000101111111110110000010010100000100011010110100001010010101010100011010111011111111010010110001000011100110111101100000010010011101000110101011001000110110001010111010101101110010101100101100101111110000110100011100110110100000101111001011101001111001100011110000100001101101100110000010001100100001110110101010000110001100000110100100011101011000001110000011100111110111000100100110001011110011010000001101011111111010011011110101010101100011110001110100101000110111000000111011010010011110111010101011100000110101111011000111101100000011111000111010011010110101100111110000011010110110101101010001010001000000001010010001011000001100100111011100101101100001010100011101011111001011001100110001001001000001001011001111011101101011110011100110110010011001000010111010010001110100101100000100100101010111010010111100101000100101011001100011010111101001000011111110010110110000011100110001011000100011100101010001011001010110010110100011010000011000000100000111000000001111100111100111011000111111001101101101101101001110001011010101001111011001110100001001110001110100001000001100110101011110100011110111100011110101011110010000001110010001010111000001001100000101100010101101000110000001111001011011001001100100100010100000110100010101100110000100100110101110011000111001101001011010011011110001001100111100011100001011111010011010010101101110101011001000001000111110000100110011101010011000100110110001010100011101010101001001001011000111101011101110001010011000101110010001111101000110000101110100001101001011110001111010101110011101111100101011011001111000010011000010001010101000100011111111110110000111000001000011100011111010001000110100111111010011110111011011111100111001111011000100101001000110101100001101100010100110110111111101100011011101111000101101100010100010001100111010001000101101110111110100010010111000000111110110010010010110000101111001110111110111100000000110111100000000000011001110110100000101100111010010101111101111111111001101011100001011111001110011000100010000101111011100110001100111010011000110011110010111011111111001001111110101101100001111010101011110111110010011001011101001000101011110100100110000111101101100111111011001000011010000111111101101010011111001001100110010001111100100110100110000111101100111011001111000000001010101010100010101010010001001110001001000010100011110111110001100011101001110011010100001000000000110101000100100101010100010100001000101010111101111000010111001100110101001010010000111010100011000101110111111000110111001010100001001101011110001101101010011110010110011111010100011101001000100111101010101011010000101011101110100001111000010011101011101100011011100100000001011100011001010111101010010111101101010100000100000001100000001010101110110011001101001101010110111011011010011111000000010101001100100010101111101000011000110011000011111000001100100010011001000110001010000010111111110100001010011101111110010110101111101100000111011101001011000001001100110011000110111000010000110001101011101100001001001100110001000000010010110010100111100011000011111111011111101100011010111010010100111000000010110001000101001100011010011000101011010000000101100001000010101111101000110011111010100010010100000000101001100001101010110100111010000000101011100000111001000000010010101100101011100110100111010110101110010100001100010110001011001011001100101101001000011101000111001100100000000100001000111010110001011110101001101100001110111101001000100110100110011100100000000001010000101011001110010110100110110001000111001110000110001010101000111100100101110101000111011001010100000001100001001000010110111101010100001000101011110000101110101111000000101010101000111011111110001100101011110000001110110101111111000001011101010101111010000100010000011101111001101000110110111010101111110100100011101011101010001101100000010101010000111110010001011101100001101010010001101110010101100001101111101011001110000001010111101010110101000011001100111110011011100011111101001011100001001110001101011100011011111101001001111100111010101111010010101001111111110110101111010101110111011101101110100001000010010011000011101101011001001
244652648608644057289603953074612700175933964244789320848667909167563692702652547784242339814218808951063055612658793570186844889979282952606878226608877859935559988556621623278752812292605612612163883997532869509885618332866441068962865470136130272926263303521750180711550883814600026677234330838029265221441047841355637850083056067511836089295432603059046109298537904428654107114553910703320935211803599935665225999452564664140186232164739291375407556107412707105250411308366447742913058979723684840765449422267902293487537073300912104264330520400754843332881465525532116517816288168335852096828051764240859454454009518120111671812902113201938369763608841769720763131720010223805776325980208111929662026610968279056747490265115993253416851071451892127738460444147514901971784385322720069737101076139227245987315223123169336706863690888801293487213146614146679028050257150777887496982927824949105219206894794372697828812226397922441686397417061966543488748591869285400239357533529688996471390996874988047597260063994210684092801510867133594884893681119353051040329814500624965262408435785310992711659906711739870502390260550767314380870394028475183756688989994399612240550769839124489086507436798682013488698499282429664122768579273

输入得到flag

1
2
3
kali@kali:~/Downloads$ ./tracing-into-the-night 
>>> 244652648608644057289603953074612700175933964244789320848667909167563692702652547784242339814218808951063055612658793570186844889979282952606878226608877859935559988556621623278752812292605612612163883997532869509885618332866441068962865470136130272926263303521750180711550883814600026677234330838029265221441047841355637850083056067511836089295432603059046109298537904428654107114553910703320935211803599935665225999452564664140186232164739291375407556107412707105250411308366447742913058979723684840765449422267902293487537073300912104264330520400754843332881465525532116517816288168335852096828051764240859454454009518120111671812902113201938369763608841769720763131720010223805776325980208111929662026610968279056747490265115993253416851071451892127738460444147514901971784385322720069737101076139227245987315223123169336706863690888801293487213146614146679028050257150777887496982927824949105219206894794372697828812226397922441686397417061966543488748591869285400239357533529688996471390996874988047597260063994210684092801510867133594884893681119353051040329814500624965262408435785310992711659906711739870502390260550767314380870394028475183756688989994399612240550769839124489086507436798682013488698499282429664122768579273
<<< dam{and_1nto_d4ta_depend3ncy}

参考资料

[1] 平方求幂–wikipedia

[2] DamCTF rev/tracing-into-the-night wp by macz–ctftime

附录

平方求幂算法汇编代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
.text:0000000000401236 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0000000000401236 public main
.text:0000000000401236 main proc near ; DATA XREF: _start+21↑o
.text:0000000000401236 ; __unwind {
.text:0000000000401236 endbr64
.text:000000000040123A push rbp
.text:000000000040123B mov rbp, rsp
.text:000000000040123E sub rsp, 650h
.text:0000000000401245 mov rax, fs:28h
.text:000000000040124E mov [rbp-8], rax
.text:0000000000401252 xor eax, eax
.text:0000000000401254 lea rdi, asc_4029E0 ; ">>> "
.text:000000000040125B mov eax, 0
.text:0000000000401260 call puts
.text:0000000000401265 lea rax, [rbp-5F0h]
.text:000000000040126C mov rsi, rax
.text:000000000040126F lea rdi, a1499s ; "%1499s"
.text:0000000000401276 mov eax, 0
.text:000000000040127B call scanf
.text:0000000000401280 lea rdx, [rbp-610h]
.text:0000000000401287 lea rsi, [rbp-620h]
.text:000000000040128E lea rax, [rbp-630h]
.text:0000000000401295 mov ecx, 0
.text:000000000040129A mov rdi, rax
.text:000000000040129D mov eax, 0
.text:00000000004012A2 call gmpz_inits
.text:00000000004012A7 lea rax, [rbp-630h]
.text:00000000004012AE mov edx, 0Ah
.text:00000000004012B3 lea rsi, N ; "447184510719975054597372033820970367631"...
.text:00000000004012BA mov rdi, rax
.text:00000000004012BD call gmpz_set_str
.text:00000000004012C2 lea rax, [rbp-620h]
.text:00000000004012C9 mov edx, 0Ah
.text:00000000004012CE lea rsi, C ; "206194863148973418443184998011923777934"...
.text:00000000004012D5 mov rdi, rax
.text:00000000004012D8 call gmpz_set_str
.text:00000000004012DD lea rcx, [rbp-5F0h]
.text:00000000004012E4 lea rax, [rbp-610h]
.text:00000000004012EB mov edx, 0Ah
.text:00000000004012F0 mov rsi, rcx
.text:00000000004012F3 mov rdi, rax
.text:00000000004012F6 call gmpz_set_str
.text:00000000004012FB lea rax, [rbp-600h]
.text:0000000000401302 mov esi, 1
.text:0000000000401307 mov rdi, rax
.text:000000000040130A call gmpz_init_set_ui
.text:000000000040130F mov rax, [rbp-608h]
.text:0000000000401316 mov [rbp-640h], rax
.text:000000000040131D mov eax, [rbp-60Ch]
.text:0000000000401323 mov [rbp-644h], eax
.text:0000000000401329 mov eax, [rbp-644h]
.text:000000000040132F sub eax, 1
.text:0000000000401332 mov [rbp-650h], eax
.text:0000000000401338 jmp loc_40145D
.text:000000000040133D ; ---------------------------------------------------------------------------
.text:000000000040133D
.text:000000000040133D loc_40133D: ; CODE XREF: main+22E↓j
.text:000000000040133D mov dword ptr [rbp-64Ch], 0
.text:0000000000401347 jmp loc_401449
.text:000000000040134C ; ---------------------------------------------------------------------------
.text:000000000040134C
.text:000000000040134C loc_40134C: ; CODE XREF: main+21A↓j
.text:000000000040134C lea rdx, [rbp-600h]
.text:0000000000401353 lea rcx, [rbp-600h]
.text:000000000040135A lea rax, [rbp-600h]
.text:0000000000401361 mov rsi, rcx
.text:0000000000401364 mov rdi, rax
.text:0000000000401367 call gmpz_mul
.text:000000000040136C lea rdx, [rbp-630h]
.text:0000000000401373 lea rcx, [rbp-600h]
.text:000000000040137A lea rax, [rbp-600h]
.text:0000000000401381 mov rsi, rcx
.text:0000000000401384 mov rdi, rax
.text:0000000000401387 call gmpz_mod
.text:000000000040138C mov eax, [rbp-650h]
.text:0000000000401392 cdqe
.text:0000000000401394 lea rdx, ds:0[rax*8]
.text:000000000040139C mov rax, [rbp-640h]
.text:00000000004013A3 add rax, rdx
.text:00000000004013A6 mov rax, [rax]
.text:00000000004013A9 mov rdx, 8000000000000000h
.text:00000000004013B3 and rdx, rax
.text:00000000004013B6 mov rax, 8000000000000000h
.text:00000000004013C0 cmp rdx, rax
.text:00000000004013C3 jnz short loc_401405
.text:00000000004013C5 lea rdx, [rbp-620h]
.text:00000000004013CC lea rcx, [rbp-600h]
.text:00000000004013D3 lea rax, [rbp-600h]
.text:00000000004013DA mov rsi, rcx
.text:00000000004013DD mov rdi, rax
.text:00000000004013E0 call gmpz_mul
.text:00000000004013E5 lea rdx, [rbp-630h]
.text:00000000004013EC lea rcx, [rbp-600h]
.text:00000000004013F3 lea rax, [rbp-600h]
.text:00000000004013FA mov rsi, rcx
.text:00000000004013FD mov rdi, rax
.text:0000000000401400 call gmpz_mod
.text:0000000000401405
.text:0000000000401405 loc_401405: ; CODE XREF: main+18D↑j
.text:0000000000401405 mov eax, [rbp-650h]
.text:000000000040140B cdqe
.text:000000000040140D lea rdx, ds:0[rax*8]
.text:0000000000401415 mov rax, [rbp-640h]
.text:000000000040141C add rax, rdx
.text:000000000040141F mov rdx, [rax]
.text:0000000000401422 mov eax, [rbp-650h]
.text:0000000000401428 cdqe
.text:000000000040142A lea rcx, ds:0[rax*8]
.text:0000000000401432 mov rax, [rbp-640h]
.text:0000000000401439 add rax, rcx
.text:000000000040143C add rdx, rdx
.text:000000000040143F mov [rax], rdx
.text:0000000000401442 add dword ptr [rbp-64Ch], 1
.text:0000000000401449
.text:0000000000401449 loc_401449: ; CODE XREF: main+111↑j
.text:0000000000401449 cmp dword ptr [rbp-64Ch], 3Fh
.text:0000000000401450 jle loc_40134C
.text:0000000000401456 sub dword ptr [rbp-650h], 1
.text:000000000040145D
.text:000000000040145D loc_40145D: ; CODE XREF: main+102↑j
.text:000000000040145D cmp dword ptr [rbp-650h], 0
.text:0000000000401464 jns loc_40133D
.text:000000000040146A mov rax, [rbp-5F8h]
.text:0000000000401471 mov [rbp-638h], rax
.text:0000000000401478 mov eax, [rbp-5FCh]
.text:000000000040147E shl eax, 3
.text:0000000000401481 mov [rbp-644h], eax
.text:0000000000401487 lea rdi, asc_4029EC ; "<<< "
.text:000000000040148E mov eax, 0
.text:0000000000401493 call puts
.text:0000000000401498 mov eax, [rbp-644h]
.text:000000000040149E sub eax, 1
.text:00000000004014A1 mov [rbp-648h], eax
.text:00000000004014A7 jmp short loc_4014D0
.text:00000000004014A9 ; ---------------------------------------------------------------------------
.text:00000000004014A9
.text:00000000004014A9 loc_4014A9: ; CODE XREF: main+2A1↓j
.text:00000000004014A9 mov eax, [rbp-648h]
.text:00000000004014AF movsxd rdx, eax
.text:00000000004014B2 mov rax, [rbp-638h]
.text:00000000004014B9 add rax, rdx
.text:00000000004014BC movzx eax, byte ptr [rax]
.text:00000000004014BF movsx eax, al
.text:00000000004014C2 mov edi, eax
.text:00000000004014C4 call put_char
.text:00000000004014C9 sub dword ptr [rbp-648h], 1
.text:00000000004014D0
.text:00000000004014D0 loc_4014D0: ; CODE XREF: main+271↑j
.text:00000000004014D0 cmp dword ptr [rbp-648h], 0
.text:00000000004014D7 jns short loc_4014A9
.text:00000000004014D9 mov eax, 0
.text:00000000004014DE mov rsi, [rbp-8]
.text:00000000004014E2 xor rsi, fs:28h
.text:00000000004014EB jz short locret_4014F2
.text:00000000004014ED call sub_4010E0
.text:00000000004014F2
.text:00000000004014F2 locret_4014F2: ; CODE XREF: main+2B5↑j
.text:00000000004014F2 leave
.text:00000000004014F3 retn
.text:00000000004014F3 ; } // starts at 401236
.text:00000000004014F3 main endp

IDA使用技巧--导入头文件和导入函数签名

做了一道题,虽然没做出来,但是学到了很多。

使用IDA导入头文件

在IDA中按Ctrl+F9,导入对应的头文件即可。这个最好在对应的操作系统下导入。

导入函数签名

使用Shift+F5键可以导入使用已经存在的函数签名

如果没有已经存在的函数签名,要自己使用Flair制作

以下教程使用libgmp.a做例子

  1. 使用pelf制作pat

    1
    ./pelf libgmp.a gmp.pat

    如果出现

    1
    Fatal [PATH_TO_libgmp.a] (mp_set_fns.o): Unknown relocation type 42 (offset in section=0x2b).

    就增加选项

    1
    ./pelf -r42:0:0 libgmp.a gmp.pat

    或者使用ar删除报错的地址,直到错误消失(未测试)

  2. 使用sigmake制作签名

    1
    ./sigmake gmp.pat gmp.sig

    如果出现

    1
    2
    gmp.sig: modules/leaves: 475/517, COLLISIONS: 2
    See the documentation to learn how to resolve collisions.

    说明有冲突,此时在目录下会生成一个gmp.exc,打开这个文件,删除注释,然后在你想保留的函数前面增加一个+

  3. 如果一切顺利,则会生成sig文件

  4. 把sig文件复制到IDA_INSTALL_PATH/sig/pc目录下,打开IDA

  5. Shift+F5,打开函数签名页面,右键,选择你刚刚添加的签名

注意:不同版本的gcc编译出的静态链接库不同,有时导入了函数还是不起作用,可能就是版本问题。

可以使用别人制作好的sig签名库

https://github.com/Maktm/FLIRTDB

https://github.com/push0ebp/sig-database

匹配最佳sig

https://github.com/maroueneboubakri/lscan

其它确定函数名的方法

调试 = =,gdb会显示使用了哪个库。如果要对对应的库调试,使用dir命令。

参考资料(不分先后)

[1] https://bbs.pediy.com/thread-175607.htm

[2] https://xz.aliyun.com/t/4484

[3] https://blog.csdn.net/qq_33438733/article/details/79968964

[4] https://blog.csdn.net/dupei/article/details/99835255

西湖论剑2020 Writeup

[Rev] Cellular.exe

唯一一道做出来的题,还是走了狗屎运。

首先是主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int __cdecl main(int argc, const char **argv, const char **envp)
{
Honeycomb *v3; // ebx
char *Format; // [esp+0h] [ebp-58h]
int i; // [esp+28h] [ebp-30h]
Honeycomb *honeyComb1; // [esp+2Ch] [ebp-2Ch]
char v8; // [esp+30h] [ebp-28h]

_alloca((size_t)Format);
__main();
memset(&v8, 0, 0x1Au);
v3 = (Honeycomb *)operator new(8u);
Honeycomb::Honeycomb(v3);
honeyComb1 = v3;
for ( i = 0; i <= 4; ++i )
Honeycomb::IncDepth(honeyComb1);
printf("Path:");
scanf("%25s", &v8);
if ( (unsigned __int8)Honeycomb::CheckFlag(honeyComb1, &v8) )
{
puts("Congrulations!");
puts("The flag is md5(Path)!");
}
else
{
puts("Wrong~");
}
system("pause");
return 0;
}

先初始化了一些结构体实例,然后读入用户输入,使用checkFlag进行校验。

checkFlag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
signed int __cdecl Honeycomb::CheckFlag(Honeycomb *this, char *Str)
{
signed int v3; // [esp+14h] [ebp-14h]
char direction; // [esp+1Bh] [ebp-Dh]
size_t i; // [esp+20h] [ebp-8h]
Hexagon *hexagon; // [esp+24h] [ebp-4h]

hexagon = this->head;
for ( i = 0; i < strlen(Str); ++i )
{
direction = Str[i];
if ( direction == 'L' )
{
if ( this->head == hexagon )
{
if ( !(unsigned __int8)Honeycomb::RotateL(this, &hexagon, 0) )
return 0;
}
else if ( !(unsigned __int8)Honeycomb::Clockwise(this, &hexagon) )
{
return 0;
}
}
else if ( direction == 'R' )
{
if ( this->head == hexagon )
{
if ( !(unsigned __int8)Honeycomb::RotateR(this, &hexagon, 0) )
return 0;
}
else if ( !(unsigned __int8)Honeycomb::Counterclockwise(this, &hexagon) )
{
return 0;
}
}
}
v3 = 0;
if ( hexagon->data == '#' && Honeycomb::CheckUseCount(this) == 94 )
v3 = 1;
return v3;
}

用户的输入只有L,R,然后读完用户的输入,走完整条路径。

根据题目提示,应该是根据用户的输入判断是否可以成功搭建蜂巢。

此时可以根据签名的初始化函数猜测结构体偏移对应的值的意义。

然后进入初始化函数,RotateL/R函数,Clockwise函数,发现太复杂了根本看不下去。

可以使用深度优先搜索算法找到所有走完迷宫的路径,然后依次尝试这些找到的路径。

插桩可以查看是否走完了对应的路径。

这里使用更为简单的复制代码然后修改,因为我不怎么会插桩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
#include <string>
#include <iostream>

using namespace std;

struct Hexagon{
int data;
int is_king;
Hexagon *fd;
Hexagon *bk;
Hexagon *d1;
};
struct Honeycomb{
Hexagon *head;
int head_num;
};
char map[] = "*-----++++--++--------------------------------+++-#-----------++-------------------+++++++++----+++---++++++++---++++---+------+++++++++++++++++++---+";
int g_num=0;
unsigned char pathL[]={0x1,0x1,0x1,0x1,0x1,0x2,0x1,0x1,0x3,0x2,0x1,0x1,0x3,0x2,0x3,0x1,0x3,0x2,0x3,0x2,0x3,0x2,0x3,0x2,0x1,0x2,0x3,0x2,0x1,0x1,0x3,0x2,0x1,0x1,0x3,0x2,0x1,0x3,0x2,0x1,0x1,0x3,0x2,0x1,0x3,0x3,0x2,0x1,0x3,0x2,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0};
signed int RotateL(Honeycomb *hc, Hexagon **a2, int a3)
{
char v4; // [esp+3h] [ebp-15h]
signed int i; // [esp+8h] [ebp-10h]
Hexagon *v7; // [esp+Ch] [ebp-Ch]
Hexagon *v8; // [esp+10h] [ebp-8h]
Hexagon *v9; // [esp+14h] [ebp-4h]

for ( i = 0; i <= 4; ++i )
{
v9 = (*a2)->fd;
v8 = (*a2)->bk;
v7 = (*a2)->d1;
v4 = pathL[5 * a3 + i];
if ( v4 == 2 )
{
if ( v8->data == '+' )
return 0;
if ( (v8->data != '-' || v8->is_king) && v9->data != 35 )
{
if ( v8->is_king == 1 )
return 1;
}
else
{
*a2 = v8;
(*a2)->is_king = 1;
}
}
else if ( v4 > 2 )
{
if ( v4 == 3 )
{
if ( v7->data == 43 )
return 0;
if ( (v7->data != 45 || v7->is_king) && v9->data != 35 )
{
if ( v7->is_king == 1 )
return 1;
}
else
{
*a2 = v7;
(*a2)->is_king = 1;
}
}
}
else if ( v4 == 1 )
{
if ( v9->data == 43 )
return 0;
if ( (v9->data != 45 || v9->is_king) && v9->data != 35 )
{
if ( v9->is_king == 1 )
return 1;
}
else
{
*a2 = v9;
(*a2)->is_king = 1;
}
}
}
return 1;
}
int __cdecl DirectionL(Honeycomb *hc, Hexagon **a2, int a3, int a4, int a5)
{
Hexagon *v7; // [esp+10h] [ebp-Ch]
Hexagon *v8; // [esp+14h] [ebp-8h]
Hexagon *v9; // [esp+18h] [ebp-4h]

v9 = (*a2)->fd;
v8 = (*a2)->bk;
v7 = (*a2)->d1;
if ( v9->is_king == 1 && v8->is_king == 1 )
return (unsigned __int8)RotateL(hc, a2, a3);
if ( v9->is_king == 1 && v7->is_king == 1 )
return (unsigned __int8)RotateL(hc, a2, a4);
if ( v8->is_king != 1 || v7->is_king != 1 )
return 0;
return (unsigned __int8)RotateL(hc, a2, a5);
}
int Clockwise(Honeycomb *hc, Hexagon **a2)
{
Hexagon *v2; // ST20_4
Hexagon *v5; // [esp+18h] [ebp-14h]
Hexagon *v6; // [esp+1Ch] [ebp-10h]
Hexagon *v7; // [esp+24h] [ebp-8h]
Hexagon *v8; // [esp+28h] [ebp-4h]

v8 = (*a2)->fd;
v7 = (*a2)->bk;
v2 = (*a2)->d1;
v6 = (*a2)->bk->fd;
v5 = (*a2)->fd->bk;
if ( v8->data == 42 || v7->data == 42 )
return (unsigned __int8)RotateL(hc, a2, 6);
if ( v6->bk->bk == *a2 && v5->fd->fd == *a2 )
return (unsigned __int8)DirectionL(hc, a2, 4, 1, 8);
if ( v8->bk == *a2 && v7->bk == *a2 )
return (unsigned __int8)DirectionL(hc, a2, 6, 7, 2);
if ( v7->fd == *a2 && v8->fd == *a2 )
return (unsigned __int8)DirectionL(hc, a2, 9, 5, 3);
if ( v6->bk->bk == *a2 )
return (unsigned __int8)DirectionL(hc, a2, 9, 1, 8);
if ( v5->fd->fd == *a2 )
return (unsigned __int8)DirectionL(hc, a2, 4, 7, 8);
return (unsigned __int8)DirectionL(hc, a2, 9, 7, 8);
}

unsigned char pathR[]={0x2,0x2,0x2,0x2,0x2,0x1,0x2,0x2,0x3,0x1,0x2,0x2,0x3,0x1,0x3,0x2,0x3,0x1,0x3,0x1,0x3,0x1,0x3,0x1,0x2,0x1,0x3,0x1,0x2,0x2,0x3,0x1,0x2,0x2,0x3,0x1,0x2,0x3,0x1,0x2,0x2,0x3,0x1,0x2,0x3,0x3,0x1,0x2,0x3,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0};
signed int RotateR(Honeycomb *hc, Hexagon **a2, int a3)
{
char v4; // [esp+3h] [ebp-15h]
signed int i; // [esp+8h] [ebp-10h]
Hexagon *v7; // [esp+Ch] [ebp-Ch]
Hexagon *v8; // [esp+10h] [ebp-8h]
Hexagon *v9; // [esp+14h] [ebp-4h]

for ( i = 0; i <= 4; ++i )
{
v9 = (*a2)->fd;
v8 = (*a2)->bk;
v7 = (*a2)->d1;
v4 = pathR[5 * a3 + i];
if ( v4 == 2 )
{
if ( v8->data == 43 )
return 0;
if ( (v8->data != '-' || v8->is_king) && v9->data != '#' )
{
if ( v8->is_king == 1 )
return 1;
}
else
{
*a2 = v8;
(*a2)->is_king = 1;
}
}
else if ( v4 > 2 )
{
if ( v4 == 3 )
{
if ( v7->data == 43 )
return 0;
if ( (v7->data != 45 || v7->is_king) && v9->data != 35 )
{
if ( v7->is_king == 1 )
return 1;
}
else
{
*a2 = v7;
(*a2)->is_king = 1;
}
}
}
else if ( v4 == 1 )
{
if ( v9->data == 43 )
return 0;
if ( (v9->data != 45 || v9->is_king) && v9->data != 35 )
{
if ( v9->is_king == 1 )
return 1;
}
else
{
*a2 = v9;
(*a2)->is_king = 1;
}
}
}
return 1;
}
int __cdecl DirectionR(Honeycomb *hc, Hexagon **a2, int a3, int a4, int a5)
{
Hexagon *v7; // [esp+10h] [ebp-Ch]
Hexagon *v8; // [esp+14h] [ebp-8h]
Hexagon *v9; // [esp+18h] [ebp-4h]

v9 = (*a2)->fd;
v8 = (*a2)->bk;
v7 = (*a2)->d1;
if ( v9->is_king == 1 && v8->is_king == 1 )
return (unsigned __int8)RotateR(hc, a2, a3);
if ( v9->is_king == 1 && v7->is_king == 1 )
return (unsigned __int8)RotateR(hc, a2, a4);
if ( v8->is_king != 1 || v7->is_king != 1 )
return 0;
return (unsigned __int8)RotateR(hc, a2, a5);
}
int Counterclockwise(Honeycomb *hc, Hexagon **a2)
{
Hexagon *v4; // [esp+18h] [ebp-10h]
Hexagon *v5; // [esp+1Ch] [ebp-Ch]
Hexagon *v6; // [esp+20h] [ebp-8h]
Hexagon *v7; // [esp+24h] [ebp-4h]

v7 = (*a2)->fd;
v6 = (*a2)->bk;
v5 = (*a2)->bk->fd;
v4 = (*a2)->fd->bk;
if ( v7->data == 42 || v6->data == 42 )
return (unsigned __int8)RotateR(hc, a2, 6);
if ( v5->bk->bk == *a2 && v4->fd->fd == *a2 )
return (unsigned __int8)DirectionR(hc, a2, 4, 8, 1);
if ( v7->bk == *a2 && v6->bk == *a2 )
return (unsigned __int8)DirectionR(hc, a2, 9, 3, 5);
if ( v6->fd == *a2 && v7->fd == *a2 )
return (unsigned __int8)DirectionR(hc, a2, 6, 2, 7);
if ( v5->bk->bk == *a2 )
return (unsigned __int8)DirectionR(hc, a2, 4, 8, 7);
if ( v4->fd->fd == *a2 )
return (unsigned __int8)DirectionR(hc, a2, 9, 8, 1);
return (unsigned __int8)DirectionR(hc, a2, 9, 8, 7);
}
int __cdecl CheckUseCount(Honeycomb *hc)
{
Hexagon *v1; // ST10_4
int l; // [esp+0h] [ebp-18h]
int j; // [esp+4h] [ebp-14h]
signed int k; // [esp+4h] [ebp-14h]
int i; // [esp+8h] [ebp-10h]
Hexagon *v7; // [esp+Ch] [ebp-Ch]
Hexagon *v8; // [esp+10h] [ebp-8h]
int v9; // [esp+14h] [ebp-4h]

v9 = 0;
v1 = hc->head;
for ( i = 0; hc->head_num > i; ++i )
{
v8 = hc->head;
for ( j = 0; j < i; ++j )
v8 = v8->d1->fd;
if ( hc->head != v8 && v8->fd->is_king == 1 )
++v9;
v7 = v8;
for ( k = 0; k <= 5; ++k )
{
if ( v8->is_king == 1 )
++v9;
v8 = v8->bk;
for ( l = 0; l < i; ++l )
{
if ( v8->is_king == 1 )
++v9;
v8 = v8->bk;
if ( v8->fd == v7 )
break;
if ( v8->is_king == 1 )
++v9;
v8 = v8->fd;
}
}
}
return v9;
}
signed int CheckFlag(Honeycomb *hc, char *Str)
{
signed int v3; // [esp+14h] [ebp-14h]
char direction; // [esp+1Bh] [ebp-Dh]
size_t i; // [esp+20h] [ebp-8h]
Hexagon *hexagon; // [esp+24h] [ebp-4h]

hexagon = hc->head;
for ( i = 0; i < strlen(Str); ++i )
{
direction = Str[i];
if ( direction == 'L' )
{
if ( hc->head == hexagon )
{
if ( !(unsigned __int8)RotateL(hc, &hexagon, 0) )
return 0;
}
else if ( !(unsigned __int8)Clockwise(hc, &hexagon) )
{
return 0;
}
}
else if ( direction == 'R' )
{
if ( hc->head == hexagon )
{
if ( !(unsigned __int8)RotateR(hc, &hexagon, 0) )
return 0;
}
else if ( !(unsigned __int8)Counterclockwise(hc, &hexagon) )
{
return 0;
}
}
}
v3 = 0;
if ( hexagon->data == '#' && CheckUseCount(hc) == 94 )
v3 = 1;
return v3;
}
Honeycomb *InsertHexagon(Honeycomb *hc, Hexagon *a2)
{
int v2; // esi
Hexagon *v3; // ebx
int v4; // esi
Hexagon *v5; // ebx
int v6; // esi
Hexagon *v7; // ebx
int v8; // esi
Hexagon *v9; // ebx
int v10; // esi
Hexagon *v11; // ebx
Honeycomb *result; // eax
int j; // [esp+8h] [ebp-20h]
signed int i; // [esp+Ch] [ebp-1Ch]
Hexagon *v15; // [esp+18h] [ebp-10h]
Hexagon *v16; // [esp+1Ch] [ebp-Ch]
Hexagon *v17; // [esp+34h] [ebp+Ch]
Hexagon *v18; // [esp+34h] [ebp+Ch]
Hexagon *v19; // [esp+34h] [ebp+Ch]
Hexagon *v20; // [esp+34h] [ebp+Ch]
Hexagon *v21; // [esp+34h] [ebp+Ch]

v15 = 0;
while ( a2->d1 )
a2 = a2->d1->fd;
for ( i = 0; i <= 5; ++i )
{
v16 = a2->bk;
v2 = map[g_num++];
v3 = new Hexagon;
v3->data=v2;
v3->data=v2;
a2->d1 = v3;
v3->d1 = a2;
v17 = v3;
if ( v15 )
{
v3->bk = v15;
v15->bk = v3;
}
v4 = map[g_num++];
v5 = new Hexagon;
v5->data=v4;
v17->fd = v5;
v5->fd = v17;
v18 = v5;
v6 = map[g_num++];
v7 = new Hexagon;
v7->data=v6;
v18->bk = v7;
v7->fd = v18;
v15 = v7;
for ( j = 0; hc->head_num > 1 && hc->head_num - 1 > j; ++j )
{
v19 = v16;
v16 = v16->bk->fd;
v8 = map[g_num++];
v9 = (Hexagon *)operator new(0x14u);
Hexagon::Hexagon(v9, v8);
v19->d1 = v9;
v9->d1 = v19;
v20 = v9;
if ( v15 )
{
v9->bk = v15;
v15->bk = v9;
}
v10 = map[g_num++];
v11 = (Hexagon *)operator new(0x14u);
Hexagon::Hexagon(v11, v10);
v20->fd = v11;
v11->fd = v20;
v15 = v11;
}
if ( v16->d1 )
{
v21 = v16->d1;
v21->bk = v15;
v15->bk = v21;
break;
}
a2 = v16;
}
result = hc;
++result->head_num;
return result;
}

signed int IncDepth(Honeycomb *hc)
{
int a2; // esi
Hexagon *v2; // ebx
int data; // esi
Hexagon *nextHexagon; // ebx
signed int i; // [esp+10h] [ebp-18h]
Hexagon *curHexagon; // [esp+14h] [ebp-14h]

if ( hc->head )
return (unsigned __int8)InsertHexagon(hc, hc->head);
a2 = map[g_num++];
v2 = new Hexagon;
v2->data=a2;
hc->head = v2;
hc->head->is_king = 1;
curHexagon = hc->head;
for ( i = 0; i <= 4; ++i )
{
data = map[g_num++];
nextHexagon = new Hexagon;
nextHexagon->data=data;
curHexagon->bk = nextHexagon;
nextHexagon->fd = curHexagon;
curHexagon = nextHexagon;
}
hc->head->fd = curHexagon;
curHexagon->bk = hc->head;
++hc->head_num;
return 1;
}
void check(){

}
Honeycomb *mHoneycomb;
void init(){
mHoneycomb = new Honeycomb;

for(size_t i=0;i<=4;i++){
IncDepth(mHoneycomb);
}
}
int main(){
init();
check();
}c

JarvisOJ 做题记录

[pwn]guestbook2

问题&读别人wp 10-6

  • unlink操作
  • glibc版本为2.28
  • 需要阅读源码
  • 局部性原理 locality

[rev]bbencode

是一个流密钥生成器,这种密钥生成器一般都有一个周期, 并且不是很大,所以直接爆破即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def bbencode(n):
a = 0
for i in bin(n)[2:]:
a = a << 1
if (int(i)):
a = a ^ n
if a >> 256:
a = a ^ 0x10000000000000000000000000000000000000000000000000000000000000223
return a
a = 61406787709715709430385495960238216763226399960658358000016620560764164045692
s = 61406787709715709430385495960238216763226399960658358000016620560764164045692
while True:
if bbencode(s)==a:
print(s.to_bytes(32,'big'))
break
s = bbencode(s)

[rev]爬楼梯

直接使用ApkIDE把onCreate的v5改成1即可获取flag

Heap Pwn Note-1 堆的结构

网上已经有很多介绍malloc的文章,这里是我阅读CTFwiki和其它资料时的笔记。

准备工作

kali安装glibc源码

1
sudo apt install glibc-source

安装后,源码压缩包位于/usr/src/glibc/目录下,可以自行解压

gdb调试时使用

1
dir PATH-TO-GLIBC_SOURCE

命令,进入malloc后就会有源码作为对照。

gdb编译程序时使用

1
gcc -ggdb source.c -o source

命令,可以使应用程序也带有调试符号,可以对照源码。

另外安装pwndbg, gef, peda可以增加调试便捷性。

在线源码阅读

https://code.woboq.org/userspace/glibc/

https://elixir.bootlin.com/glibc/glibc-2.31/source

实际操作中目标平台的libc版本可能和本地版本不同,pwntools使用如下命令更改libc

1
io = process(['./bin'],env={"LD_PRELOAD":"./libc.so.6"})

堆概述

malloc函数返回对应大小字节的内存块指针

  • n=0,返回最小内存块
  • n<=0,由于转成size_t(多数情况下为无符号数)会非常大,通常来说由于没有足够空间而失败。

free函数释放指针对应的内存块,可能是malloc或realloc得到的

  • p=NULL,不执行任何操作
  • p已被释放,会出现乱七八糟的结果(double free)。所以指针释放后让它指向NULL
  • 释放很大内存空间时会归还一部分给系统。

内存分配背后的系统调用

  • (s)brk 程序break的地方,可以理解为堆的顶端
  • mmap,munmap

程序内存图像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
----+-----------------------------------------------------
1GB |Kernel Space
----+----------------------------------------------------
|Random Stack offset
|
+------------------------------------------------------
3GB |Stack
|Grows Down
+----------------------------------------------------
|
+-----------------------------------------------------
|Memory Mapping Segment
|File Mapping and anonymous mappings
|Grows Down
+----------------------------------------------------
|
+-------------brk-----------------------------------
|Heap
|Grows Up
+-------------Start_brk-----------------------------
|random brk of offset
+--------------------------------------------------
|BSS(Block Start by Symbol) Segment
|Uninitialized static variable,filled with 0
+-------------------------------------------------
|Data Segment
|Static variable initialized by programmer
+-------------------------------------------------
|Text Segment
|Instructions
----+-------------------------------------------------

malloc_chunk

申请的内存块使用malloc_chunk表示,无论大小,无论处于分配还是释放状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/

struct malloc_chunk {

INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};

由此可知,malloc_chunk的内存图像如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
size_t位宽
-----------+<----------------chunk-----------------------------
prev_size | is_last_free(physical_nibor)?nibor_size:nibor_data
-----------+----------------------------------------------------
size | size|NON_MAIN_ARENA|IS_MAPPED|PREV_INUSE
-----------+<---malloc returning pointer-------------------------
fd |
-----------+
bk |
-----------+ is_used?user_data:fd/bk/fd_nextsize/bk_nextsize
fd_nextsize|
-----------+
bk_nextsize|
-----------+------------------------------------------------------
user_data |
-----------+------------------------------------------------------
  • prev_size

    与该chunk物理相邻的前一chunk如果时空闲,记录前一个chunk大小,包括头,否则做前一chunk数据

  • size

    该chunk大小,必须是2*SIZE_SZ的整数倍,所以低3bit对chunk大小无影响,分别表示

    • NON_MAIN_ARENA 当前chunk是否不属于主线程 1不属于,0属于
    • IS_MAPPED 当前chunk是否由mmap分配
    • PREV_INUSE 前一chunk是否被分配(使用),第一个chunk的P位为1防止非法访存。P为0时可以使用prev_size获取上一个chunk的大小和地址,方便合并
  • fd bk

    chunk被分配时,fd开始为用户数据。空闲时,会被添加到对应的管理链表

    • fd: 下一个(非物理相邻)空闲chunk
    • bk: 上一个(非物理相邻)空闲chunk
  • fd_nextsize, bk_nextsize

    只用于较大chunk

    • fd_nextsize

      指向前一个与当前chunk大小不同的第一个空闲块,不包含bin头指针

    • bk_nextsize

      指向后一个与当前chunk大小不同的第一个空闲块,不包含bin头指针

    空闲的large chunk在fd的遍历顺序中,按照从小到大的顺序遍历

malloc_state

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
have_fastchunks indicates that there are probably some fastbin chunks.
It is set true on entering a chunk into any fastbin, and cleared early in
malloc_consolidate. The value is approximate since it may be set when there
are no fastbin chunks, or it may be clear even if there are fastbin chunks
available. Given it's sole purpose is to reduce number of redundant calls to
malloc_consolidate, it does not affect correctness. As a result we can safely
use relaxed atomic accesses.
*/

struct malloc_state
{
/* Serialize access. */ // 支持多线程
__libc_lock_define (, mutex);

/* Flags (formerly in max_fast). */
int flags;

/* Set if the fastbin chunks contain recently inserted free blocks. */
/* Note this is a bool but not all targets support atomics on booleans. */
int have_fastchunks;

/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];

/* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top;

/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;

/* Normal bins packed as described above */
mchunkptr bins[NBINS * 2 - 2]; // default NBINS=128

/* Bitmap of bins */
unsigned int binmap[BINMAPSIZE];

/* Linked list */
struct malloc_state *next;

/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free;

/* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
INTERNAL_SIZE_T attached_threads;

/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};
  • 所有内部状态都位于malloc_state实例中,除非
    • USE_MALLOC_LOCK 被定义,mALLOC_MUTEx在上方声明
      • mmap不支持匿名映射,会有假的文件描述符

bin

glibc算法介绍

The main properties of the algorithms are:

  • For large (>= 512 bytes) requests, it is a pure best-fit allocator,
    with ties normally decided via FIFO (i.e. least recently used).
  • For small (<= 64 bytes by default) requests, it is a caching
    allocator, that maintains pools of quickly recycled chunks.
  • In between, and for combinations of large and small requests, it does
    the best it can trying to meet both goals at once.
  • For very large requests (>= 128KB by default), it relies on system
    memory mapping facilities, if supported.

一些值

OPTION DEFAULT VALUE

Compilation Environment options:

HAVE_MREMAP                0

Changing default word sizes:

INTERNAL_SIZE_T            size_t

Configuration and functionality options:

USE_PUBLIC_MALLOC_WRAPPERS NOT defined
USE_MALLOC_LOCK            NOT defined
MALLOC_DEBUG               NOT defined
REALLOC_ZERO_BYTES_FREES   1
TRIM_FASTBINS              0

Options for customizing MORECORE:

MORECORE                   sbrk
MORECORE_FAILURE           -1
MORECORE_CONTIGUOUS        1
MORECORE_CANNOT_TRIM       NOT defined
MORECORE_CLEARS            1
MMAP_AS_MORECORE_SIZE      (1024 * 1024)

Tuning options that are also dynamically changeable via mallopt:

DEFAULT_MXFAST             64 (for 32bit), 128 (for 64bit)
DEFAULT_TRIM_THRESHOLD     128 * 1024
DEFAULT_TOP_PAD            0
DEFAULT_MMAP_THRESHOLD     128 * 1024
DEFAULT_MMAP_MAX           65536
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*
Bins
An array of bin headers for free chunks. Each bin is doubly
linked. The bins are approximately proportionally (log) spaced.
There are a lot of these bins (128). This may look excessive, but
works very well in practice. Most bins hold sizes that are
unusual as malloc request sizes, but are more usual for fragments
and consolidated sets of chunks, which is what these bins hold, so
they can be found quickly. All procedures maintain the invariant
that no consolidated chunk physically borders another one, so each
chunk in a list is known to be preceeded and followed by either
inuse chunks or the ends of memory.
Chunks in bins are kept in size order, with ties going to the
approximately least recently used chunk. Ordering isn't needed
for the small bins, which all contain the same-sized chunks, but
facilitates best-fit allocation for larger chunks. These lists
are just sequential. Keeping them in order almost never requires
enough traversal to warrant using fancier ordered data
structures.
Chunks of the same size are linked with the most
recently freed at the front, and allocations are taken from the
back. This results in LRU (FIFO) allocation order, which tends
to give each chunk an equal opportunity to be consolidated with
adjacent freed chunks, resulting in larger free chunks and less
fragmentation.
To simplify use in double-linked lists, each bin header acts
as a malloc_chunk. This avoids special-casing for headers.
But to conserve space and improve locality, we allocate
only the fd/bk pointers of bins, and then use repositioning tricks
to treat these as the fields of a malloc_chunk*.
*/

typedef struct malloc_chunk *mbinptr; // bin是chunk链表
/* addressing -- note that bin_at(0) does not exist */
#define bin_at(m, i) \
(mbinptr) (((char *) &((m)->bins[((i) - 1) * 2])) \
- offsetof (struct malloc_chunk, fd))
/* analog of ++bin */
#define next_bin(b) ((mbinptr) ((char *) (b) + (sizeof (mchunkptr) << 1)))
/* Reminders about list directionality within bins */
#define first(b) ((b)->fd)
#define last(b) ((b)->bk)
/*
Indexing
Bins for sizes < 512 bytes contain chunks of all the same size, spaced
8 bytes apart. Larger bins are approximately logarithmically spaced:
64 bins of size 8
32 bins of size 64
16 bins of size 512
8 bins of size 4096
4 bins of size 32768
2 bins of size 262144
1 bin of size what's left
There is actually a little bit of slop in the numbers in bin_index
for the sake of speed. This makes no difference elsewhere.
The bins top out around 1MB because we expect to service large
requests via mmap.
Bin 0 does not exist. Bin 1 is the unordered list; if that would be
a valid chunk size the small bins are bumped up one.
*/
#define NBINS 128
#define NSMALLBINS 64
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ)
#define MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
#define in_smallbin_range(sz) \
((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE)
#define smallbin_index(sz) \
((SMALLBIN_WIDTH == 16 ? (((unsigned) (sz)) >> 4) : (((unsigned) (sz)) >> 3))\
+ SMALLBIN_CORRECTION)
#define largebin_index_32(sz) \
(((((unsigned long) (sz)) >> 6) <= 38) ? 56 + (((unsigned long) (sz)) >> 6) :\
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\
126)
#define largebin_index_32_big(sz) \
(((((unsigned long) (sz)) >> 6) <= 45) ? 49 + (((unsigned long) (sz)) >> 6) :\
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\
126)
// XXX It remains to be seen whether it is good to keep the widths of
// XXX the buckets the same or whether it should be scaled by a factor
// XXX of two as well.
#define largebin_index_64(sz) \
(((((unsigned long) (sz)) >> 6) <= 48) ? 48 + (((unsigned long) (sz)) >> 6) :\
((((unsigned long) (sz)) >> 9) <= 20) ? 91 + (((unsigned long) (sz)) >> 9) :\
((((unsigned long) (sz)) >> 12) <= 10) ? 110 + (((unsigned long) (sz)) >> 12) :\
((((unsigned long) (sz)) >> 15) <= 4) ? 119 + (((unsigned long) (sz)) >> 15) :\
((((unsigned long) (sz)) >> 18) <= 2) ? 124 + (((unsigned long) (sz)) >> 18) :\
126)
#define largebin_index(sz) \
(SIZE_SZ == 8 ? largebin_index_64 (sz) \
: MALLOC_ALIGNMENT == 16 ? largebin_index_32_big (sz) \
: largebin_index_32 (sz))
#define bin_index(sz) \
((in_smallbin_range (sz)) ? smallbin_index (sz) : largebin_index (sz))

unsorted bin

1
#define unsorted_chunks(M)          (bin_at (M, 1))

所有分配内存后分割剩下的chunk,free函数释放的内存块,malloc_consolidate合并的内存块,都会先放在unsorted bin内,在它们返回自己的bin时在这里还有机会再次使用。

top chunk

1
#define initial_top(M)              (unsorted_chunks (M))
  • top chunk位于可用内存边缘(如果是(s)brk分配的内存,则top chunk上边缘在brk)
  • 只有其他chunk都无法满足条件时才会使用该chunk
  • 第一次malloc会强制伸展top chunk
  • 当top chunk过大时,会返还给system
  • 初始化时对待top chunk为unsorted chunk

fast bin

small bin

large bin

unsorted bin

top chunk

将一个双向链表中的一个元素取出来,可能在以下地方使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* Take a chunk off a bin list.  */
static void
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");
fd->bk = bk;
bk->fd = fd;
if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if (p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr ("corrupted double-linked list (not small)");
if (fd->fd_nextsize == NULL)
{
if (p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}

参考资料

[1] glibc源码下载&阅读地址

B01lers Writeup(部分)

[Rev] superspy

google搜索Reversing Gameboy Advaced,找到这篇文章

https://medium.com/@bruno.macabeus/reverse-engineering-a-gameboy-advance-game-introduction-ec185bd8e02

使用文章里的debug工具,根据指引,发现屏幕显示的数据中,如果位01 00 则是背景,如果为

00 03 00 03 00 03 00 0B 00```则为台阶
1

根据这个规律,使用二进制编辑工具打开super-spy.gba,搜索hex串01 00 01 00,找到之后可以看到有个地方正好满足这一规律,并且经过改动实验发现这里存储的就是地图数据。

这样就可以改动最上面两个墙的长度,从而轻松拿到金币,获取flag