GFSJ0231-【pingpong】
读代码/Hook/模拟执行
main函数 能看到这个是一个判断 为什么是判断呢 真正的运算在so层
package com.geekerchina.pingpongmachine;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
/* JADX INFO: loaded from: classes.dex */
public class MainActivity extends AppCompatActivity {
public int p = 0;
public int num = 0;
public int ttt = 1000000;
public int tt = this.ttt;
View.OnClickListener jping = new View.OnClickListener() { // from class: com.geekerchina.pingpongmachine.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
if (MainActivity.this.tt % 2 == 1) {
MainActivity.this.p = 0;
MainActivity.this.num = 0;
MainActivity.this.tt = MainActivity.this.ttt;
}
MainActivity mainActivity = MainActivity.this;
mainActivity.tt--;
MainActivity.this.p = MainActivity.this.ping(MainActivity.this.p, MainActivity.this.num);
MainActivity.this.num++;
if (MainActivity.this.num >= 7) {
MainActivity.this.num = 0;
}
TextView t = (TextView) MainActivity.this.findViewById(R.id.out);
t.setText("PING");
if (MainActivity.this.tt == 0) {
t.setText("FLAG: BCTF{MagicNum" + Integer.toString(MainActivity.this.p) + "}");
}
}
};
View.OnClickListener jpong = new View.OnClickListener() { // from class: com.geekerchina.pingpongmachine.MainActivity.2
@Override // android.view.View.OnClickListener
public void onClick(View v) {
if (MainActivity.this.tt % 2 == 0) {
MainActivity.this.p = 0;
MainActivity.this.num = 0;
MainActivity.this.tt = MainActivity.this.ttt;
}
MainActivity mainActivity = MainActivity.this;
mainActivity.tt--;
MainActivity.this.p = MainActivity.this.pong(MainActivity.this.p, MainActivity.this.num);
MainActivity.this.num++;
if (MainActivity.this.num >= 7) {
MainActivity.this.num = 0;
}
TextView t = (TextView) MainActivity.this.findViewById(R.id.out);
t.setText("PONG");
if (MainActivity.this.tt == 0) {
t.setText("FLAG: BCTF{MagicNum" + Integer.toString(MainActivity.this.p) + "}");
}
}
};
public native int ping(int i, int i2);
public native int pong(int i, int i2);
@Override // android.support.v7.app.AppCompatActivity, android.support.v4.app.FragmentActivity, android.support.v4.app.BaseFragmentActivityGingerbread, android.app.Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = (Button) findViewById(R.id.button);
button1.setOnClickListener(this.jping);
Button button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(this.jpong);
}
@Override // android.app.Activity
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override // android.app.Activity
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
static {
System.loadLibrary("pp");
}
}
java层总共做了四件事
- 加载 native 库
libpp.so
static {
System.loadLibrary("pp");
}
- 声明两个 native 函数
ping()和pong()
public native int ping(int i, int i2);
public native int pong(int i, int i2);
- 控制 PING / PONG 按钮的点击顺序
要知道为什么是控制 首先要看数据定义的意义是什么
public int p = 0; //在so层计算后的 flag
public int num = 0; // 记录当前轮号
public int ttt = 1000000; //总次数
public int tt = this.ttt; // 剩余次数
if (MainActivity.this.tt % 2 == 1) { // 判断奇偶来执行ping还是pong
MainActivity.this.p = 0;
MainActivity.this.num = 0;
MainActivity.this.tt = MainActivity.this.ttt;
}
- 跑满 1000000 次后拼接 flag
if (MainActivity.this.tt == 0) {
t.setText("FLAG: BCTF{MagicNum" + Integer.toString(MainActivity.this.p) + "}");
}
然后我们就知道java层就这些东西 继续往so层看 能看到两个明显的函数名 ping
int __fastcall Java_com_geekerchina_pingpongmachine_MainActivity_ping(int a1, int a2, int a3, int a4)
{
int v5; // r7
int v6; // r1
int v7; // r5
double v8; // kr00_8
int v9; // r0
double v11; // [sp+4h] [bp-18h]
bool v13; // [sp+18h] [bp-4h]
v5 = 1;
v11 = 2.0;
v6 = 313249186;
v7 = 3;
v8 = 2.0;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
if ( v6 <= -1005608100 )
{
v7 *= 10;
--v5;
goto LABEL_2;
}
if ( v6 <= 1928989736 )
break;
LABEL_2:
v6 = -438792027;
}
if ( v6 <= 1704470066 )
break;
v6 = 1928989737;
v7 = 1;
v5 = a4;
}
if ( v6 <= 415380998 )
break;
j_sleep(1u);
v6 = -189238556;
if ( ((a3 ^ 0xFFFFFFFE) & a3) != 0 )
v6 = -251125011;
}
if ( v6 <= 313249185 )
break;
v6 = -1005608099;
}
if ( v6 <= 240723450 )
break;
v6 = 1704470067;
}
if ( v6 <= -189238557 )
break;
++a3;
v6 = -251125011;
}
if ( v6 > -981047242 )
break;
v13 = v8 > 1.0e-15;
v6 = -780852882;
}
if ( v6 <= -843883946 )
return (int)(v11 * (double)v7) % 10 + a3;
if ( v6 > -819957023 )
{
if ( v6 > -438792028 )
{
if ( v6 > -358730882 )
{
if ( v6 == -251125011 )
{
v6 = -843883945;
if ( a3 % 3 )
v6 = -981047241;
}
else
{
v8 = v8 * (double)v5 / (double)v7;
v11 = v11 + v8;
++v5;
v7 += 2;
v6 = 313249186;
}
}
else
{
v6 = -1962497484;
v9 = 415380999;
if ( v5 <= 0 )
LABEL_30:
v6 = v9;
}
}
else
{
v6 = -248256714;
v9 = 240723451;
if ( !v13 )
goto LABEL_30;
}
}
else
{
++a3;
v6 = -981047241;
}
}
}
pong
int __fastcall Java_com_geekerchina_pingpongmachine_MainActivity_pong(int a1, int a2, int a3, int a4)
{
int v6; // r1
int v7; // r7
int v8; // r0
_BOOL4 v9; // r2
int v10; // r0
double v12; // [sp+0h] [bp-1Ch]
double v13; // [sp+8h] [bp-14h]
int v14; // [sp+10h] [bp-Ch]
bool v15; // [sp+14h] [bp-8h]
bool v16; // [sp+18h] [bp-4h]
v12 = 1.0;
v6 = -507462074;
v7 = 1;
v13 = 1.0;
v14 = 1;
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( 1 )
{
while ( v6 > 1949801889 )
{
j_sleep(1u);
v6 = -733776665;
v8 = 775723035;
if ( (a3 & 1) != 0 )
goto LABEL_2;
}
if ( v6 > -1611537167 )
break;
v15 = a4 > 0;
v6 = 1023688921;
}
if ( v6 <= 1754059917 )
break;
v6 = -507462074;
}
if ( v6 <= 1584370720 )
break;
--a3;
v6 = -370984158;
}
if ( v6 > -1548318688 )
break;
v9 = v16;
v6 = 1584370721;
v8 = -370984158;
LABEL_12:
if ( !v9 )
LABEL_2:
v6 = v8;
}
if ( v6 > -1239423608 )
break;
v16 = a3 % 3 == 0;
v6 = -1611537166;
}
if ( v6 > -778645111 )
break;
v10 = -1129478008;
LABEL_24:
if ( v6 == v10 )
v6 = -147393298;
}
if ( v6 > -733776666 )
break;
v14 *= 10;
--a4;
v6 = -1129478008;
}
if ( v6 > -538408169 )
break;
v6 = -308568913;
}
if ( v6 > -370984159 )
break;
v6 = 95401117;
if ( 1.0 / v13 < 0.00000001 )
v6 = -163087971;
}
if ( v6 <= -308568914 )
return (int)(v12 * (double)v14) % 10 + a3;
if ( v6 > -163087972 )
{
if ( v6 <= -147393299 )
{
v10 = -163087971;
goto LABEL_24;
}
if ( v6 <= -52381000 )
{
v6 = -1784587527;
}
else if ( v6 > 95401116 )
{
if ( v6 > 142578153 )
{
if ( v6 <= 1023688920 )
{
v6 = -1506201508;
}
else
{
if ( v6 <= 1125441449 )
{
v9 = v15;
v6 = -52380999;
v8 = 1949801890;
goto LABEL_12;
}
if ( v6 == 1125441450 )
{
v6 = 775723035;
}
else
{
v12 = v12 + 1.0 / v13;
v13 = v13 * (double)++v7;
v6 = 1754059918;
}
}
}
else
{
v6 = 1282790847;
}
}
else
{
v6 = -778645110;
}
}
else
{
++a3;
v6 = 1125441450;
}
}
}
能看到这两个函数都执行了差不多的操作 看了看 没看明白 有混淆 然后上手算了一下就明白了 由于ping函数定义了一个
v5 = 1;
v11 = 2.0;
v7 = 3;
v8 = 2.0;
然后能看到他后面进行了一些操作
v8 = v8 * (double)v5 / (double)v7;
v11 = v11 + v8;
++v5;
v7 += 2;
然后算出来 一个派 3.1415926
def ping_digit(num):
v5=1
v11=2.0
v7=3
v8=2.0
while True:
v13 = v8 > 1.0e-15
if not v13:
break
v8 = v8 * v5 / v7
v11 = v11 + v8
v5 += 1
v7 += 2
return v11
for n in range(7):
print(n, ping_digit(n))
我们现在知道 a3是传进来的p a4是num v11是派 然后到返回了 这个能看出来就知道了
return (int)(v11 * (double)v7) % 10 + a3;
在上面我们就只剩v7 我们不知道 然后在往上看
v7 = 1;
v5 = a4;
然后再后面
v7 *= 10;
--v5;
能看到两个1 * 10 = 10 同时我们已经知道a4是num
num = 0
v7 = 1
循环 0 次
所以 v7 = 1
num = 1
v7 = 1
循环 1 次:v7 = 1 * 10 = 10
num = 2
v7 = 1
第 1 次:v7 = 10
第 2 次:v7 = 100
一次类推 所以就是
v7 = 10^num
最后
v7 = 1
(int)(3.141592 * 1) % 10
取到
3
按程序取法
num = 0 > 3
num = 1 > 1
num = 2 > 4
num = 3 > 1
num = 4 > 5
num = 5 > 9
num = 6 > 2
也就是
ping_digit = [3, 1, 4, 1, 5, 9, 2]
继续看 ping 函数里面和 a3 有关的代码
这里
if ( ((a3 ^ 0xFFFFFFFE) & a3) != 0 )
v6 = -251125011;
这个判断可以化简成判断 a3 的奇偶 如果 a3 是偶数,程序会走到
++a3;
也就是
if (p % 2 == 0) {
p++;
}
后面还有
if ( a3 % 3 )
v6 = -981047241;
结合上下文逻辑可以看出 如果 a3 % 3 == 0 会再次执行
++a3;
所以 ping 对 p 的修正逻辑是
if (p % 2 == 0) {
p++;
}
if (p % 3 == 0) {
p++;
}
最后再加上 π 的某一位数字 所以 ping 可以简化成
pi_digits = [3, 1, 4, 1, 5, 9, 2]
def ping(p, num):
if p % 2 == 0:
p += 1
if p % 3 == 0:
p += 1
return p + pi_digits[num]
pong同理
所以我们能想到三种解题方案
- 直接运行他的代码就能得到
pi_digits = [3, 1, 4, 1, 5, 9, 2]
e_digits = [2, 7, 1, 8, 2, 8, 1]
def ping(p, num):
if p % 2 == 0:
p += 1
if p % 3 == 0:
p += 1
return p + pi_digits[num]
def pong(p, num):
if p % 2 == 0:
p += 1
if p % 3 == 0:
p -= 1
return p + e_digits[num]
p = 0
num = 0
tt = 1000000
while tt > 0:
if tt % 2 == 0:
p = ping(p, num)
else:
p = pong(p, num)
tt -= 1
num = (num + 1) % 7
print("BCTF{MagicNum%d}" % p)
- unidbg 模拟执行
这个超级超级慢 记得要用 JDK 8 哦
把 libpp.so 放到
unidbg-android/src/test/resources/libpp.so
然后新建 Java 文件
unidbg-android/src/test/java/com/ctf/PingPongSolve.java
代码如下
package com.ctf;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.Symbol;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import java.io.File;
public class PingPongSolve {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
private final Symbol ping;
private final Symbol pong;
public PingPongSolve() {
emulator = AndroidEmulatorBuilder
.for32Bit()
.setProcessName("com.geekerchina.pingpongmachine")
.build();
Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM();
DalvikModule dm = vm.loadLibrary(
new File("unidbg-android/src/test/resources/libpp.so"),
false
);
module = dm.getModule();
ping = module.findSymbolByName(
"Java_com_geekerchina_pingpongmachine_MainActivity_ping",
false
);
pong = module.findSymbolByName(
"Java_com_geekerchina_pingpongmachine_MainActivity_pong",
false
);
if (ping == null) {
throw new RuntimeException("ping symbol not found");
}
if (pong == null) {
throw new RuntimeException("pong symbol not found");
}
}
private int callPing(int p, int num) {
Number ret = ping.call(
emulator,
vm.getJNIEnv(),
0,
p,
num
);
return ret.intValue();
}
private int callPong(int p, int num) {
Number ret = pong.call(
emulator,
vm.getJNIEnv(),
0,
p,
num
);
return ret.intValue();
}
public void solve() {
int tt = 1000000;
int p = 0;
int num = 0;
while (tt > 0) {
if (tt % 2 == 0) {
p = callPing(p, num);
} else {
p = callPong(p, num);
}
tt--;
num++;
if (num >= 7) {
num = 0;
}
}
System.out.println("result = " + p);
System.out.println("flag = BCTF{MagicNum" + p + "}");
}
public static void main(String[] args) {
PingPongSolve solve = new PingPongSolve();
solve.solve();
}
}
- hook
这个是可能会失败的 因为在虚拟机中的环境和libpp.so的架构不一样 libpp.so的架构是ARM32 我的虚拟机架构师x86_64 所以没执行成功 然后我让ai解决了一下 还是不行 遂放一个万能用脚本
import frida
import sys
import subprocess
import time
PACKAGE_NAME = "com.geekerchina.pingpongmachine"
def on_message(message, data):
if message["type"] == "send":
print("[*]", message["payload"])
elif message["type"] == "error":
print("[!] JS Error:")
print(message.get("stack", message))
else:
print(message)
jscode = r"""
function enumModules() {
try {
var r = Process.enumerateModules();
if (r !== null && r.length !== undefined) {
return r;
}
} catch (e) {
}
var mods = [];
try {
Process.enumerateModules({
onMatch: function (m) {
mods.push(m);
},
onComplete: function () {
}
});
} catch (e2) {
send("enumerateModules error: " + e2);
}
return mods;
}
function findLibPP() {
var mods = enumModules();
for (var i = 0; i < mods.length; i++) {
var name = String(mods[i].name);
var path = String(mods[i].path);
if (name === "libpp.so" || path.indexOf("libpp.so") >= 0) {
return mods[i];
}
}
return null;
}
function runCalc() {
var so = findLibPP();
if (so === null) {
send("还没找到 libpp.so,继续等待。你可以点一下 PING 或 PONG 按钮。");
setTimeout(runCalc, 500);
return;
}
send("找到 libpp.so");
send("name: " + so.name);
send("base: " + so.base);
send("size: " + so.size);
send("path: " + so.path);
send("arch: " + Process.arch);
var thumb = 0;
if (Process.arch === "arm") {
thumb = 1;
}
var pingAddr = so.base.add(0x1308 + thumb);
var pongAddr = so.base.add(0x1564 + thumb);
send("ping addr: " + pingAddr);
send("pong addr: " + pongAddr);
var ping = new NativeFunction(
pingAddr,
"int",
["pointer", "pointer", "int", "int"]
);
var pong = new NativeFunction(
pongAddr,
"int",
["pointer", "pointer", "int", "int"]
);
var env = ptr(0);
var obj = ptr(0);
var check = 1000000;
var beFlag = 0;
var num = 0;
send("开始计算");
while (true) {
if (check % 2 === 1) {
check--;
beFlag = pong(env, obj, beFlag, num);
num++;
if (num >= 7) {
num = 0;
}
} else {
check--;
beFlag = ping(env, obj, beFlag, num);
num++;
if (num >= 7) {
num = 0;
}
}
if (check === 0) {
send("check: " + check + " num: " + num);
send("FLAG : BCTF{MagicNum" + beFlag + "}");
break;
}
}
}
setImmediate(function () {
send("script loaded");
send("Java object exists: " + (typeof Java !== "undefined"));
runCalc();
});
"""
def run_cmd(cmd):
try:
return subprocess.check_output(
cmd,
shell=True,
stderr=subprocess.DEVNULL
).decode(errors="ignore").strip()
except Exception:
return ""
def start_app():
print("[*] 正在启动 App")
subprocess.run(
f"adb shell monkey -p {PACKAGE_NAME} -c android.intent.category.LAUNCHER 1",
shell=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
time.sleep(1)
def get_pid():
result = run_cmd(f"adb shell pidof {PACKAGE_NAME}")
if result:
return int(result.split()[0])
return None
def main():
start_app()
pid = get_pid()
if pid is None:
print("[-] 没找到进程")
print("[-] 请检查包名:")
print(f" adb shell pm list packages | findstr geeker")
sys.exit(1)
print("[*] PID:", pid)
device = frida.get_usb_device(timeout=5)
session = device.attach(pid)
script = session.create_script(jscode)
script.on("message", on_message)
print("[*] Running")
script.load()
sys.stdin.read()
if __name__ == "__main__":
main()
flag
BCTF{MagicNum4500009}
评论