吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5528|回复: 11
收起左侧

[Android 原创] 论坛里的crackme 练手留念杂文2

[复制链接]
shuaiyue 发表于 2019-4-24 00:12
本帖最后由 shuaiyue 于 2019-4-24 00:20 编辑

下载:https://www.52pojie.cn/forum.php ... peid%26typeid%3D262

双进程守护,无法反调试,肉疼,换了N多版本和真机无法运行。谁知道为啥的回复下,最后用夜神模拟器试试DDMS无法显示进程,各种肉疼,无奈索性静态分析吧。
乱糟糟的,凑合看吧。
流程:jadx加载dex  - > 分析入口函数



private native boolean check(String str);     //so


    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView((int) R.layout.activity_main);
        this.mEdit = (EditText) findViewById(R.id.mKeyEdit);
        this.mButton = (Button) findViewById(R.id.mCheckBtn);
        this.mButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                String flag = MainActivity.this.mEdit.getText().toString();
                if (MainActivity.this.check(flag)) {                   //JAVA层爆破
                    Toast.makeText(MainActivity.this, "great:flag{" + flag + "}", 0).show();
                } else {
                    Toast.makeText(MainActivity.this, "Error, try again", 0).show();
                }
            }
        });

在继续跟踪so在哪里加载的

//注意这里JAVA层运行先执行APP再到-》MainActivity
public class App extends Application {    //主要是做初始化用的
    private native int checkDebugger();         //动态函数


    static {
        System.loadLibrary("check");               
    }


    protected void attachBaseContext(Context arg1) {
        super.attachBaseContext(arg1);
    }


    public void onCreate() {
        super.onCreate();
        if (checkDebugger() != 0) {                         //第一次执行
        }
    }


接着到IDA 分析so 先看看checkDebugger()函数 代码很长简化下
int checkDebugger()
{
  signed int mark; // r7
  int v1; // r9
  __pid_t v2; // r0
  _BOOL4 v3; // r3
  int cid; // r0
  int v5; // r1
  int v6; // r2
  int v7; // r4
  int v8; // r0
  __pid_t fpid; // [sp+Ch] [bp-44h]
  char v11; // [sp+10h] [bp-40h]
  int v12; // [sp+14h] [bp-3Ch]
  char v13; // [sp+18h] [bp-38h]
  char v14; // [sp+19h] [bp-37h]
  char v15; // [sp+1Ah] [bp-36h]
  char v16; // [sp+1Bh] [bp-35h]
  char v17; // [sp+1Ch] [bp-34h]
  char v18; // [sp+1Dh] [bp-33h]
  char v19; // [sp+1Eh] [bp-32h]
  char v20; // [sp+1Fh] [bp-31h]
  char v21; // [sp+20h] [bp-30h]
  char v22; // [sp+21h] [bp-2Fh]
  char v23; // [sp+22h] [bp-2Eh]
  char v24; // [sp+23h] [bp-2Dh]
  char v25; // [sp+24h] [bp-2Ch]
  char v26; // [sp+25h] [bp-2Bh]
  char v27; // [sp+26h] [bp-2Ah]
  char v28; // [sp+27h] [bp-29h]


  mark = 6;
LABEL_2:
  if ( --mark && !pipe(&gpipe) )                // 对于整数 ,当n为0时,转换为布尔值就是假,此时 !n 就是 真 进入if后面的语句块
                                                // fd[0]指向管道的读端,fd[1]指向管道的写端
  {
    fpid = fork();                              // 系统先给新的进程分配资源_简单理解克隆自己创建一个新的进程
    prctl(4, 1);                                // 获取子进程接收器
    v1 = fpid;
    if ( !fpid )                                // 为真子进程干活了。
    {
      cid = getppid();                          // 子进程ID
      safe_attach(cid, v5, v6);                 // 安全附加
      close(gpipe);                             // 关闭管道
      v7 = dword_3034;
      memset(&v13, v1, 0x11u);                  // 初始化数组
      v13 = 74;
      v14 = 117;
      v15 = 115;
      v16 = 116;
      v17 = 72;
      v18 = 97;
      v19 = 118;
      v20 = 101;
      v21 = 65;
      v22 = 84;
      v23 = 114;
      v24 = 121;
      v25 = 33;
      v26 = 33;
      v27 = 33;
      v28 = 33;
      write(dword_3034, &v13, 0x10u);           // 子进程向管道里写数据
                                                //  arrys[] = {74,117,115,116,72,97,118,101,65,84,114,121,33,33,33,33}
      v8 = close(v7);                           // 成功执行后会返回0,否则返回-1
      handle_events(v8);                        // 处理接收到的连接
      exit(1);
    }
    close(dword_3034);
    pthread_create(&v11, 0, parent_read_thread, &fpid);// 父进程--线程管道读取
    do
    {
      v2 = waitpid(fpid, &v12, 1);
      if ( v2 > 0 )
        v3 = (v12 - 1) <= 0;                    // 正常
      else
        v3 = v2 == 0;                           // 检测到子进程在玩火
      if ( !v3 )
      {
        kill(fpid, 9);                          // 强制杀死子进程
        goto LABEL_2;                           // 在去制造个儿子
      }
    }
    while ( !sflag[6] );                        // 0循环,非0退出   ,一直在循环
    pthread_create(&v11, 0, child_attach_thread, &fpid);// 监控子线程
  }
  return 0;
}



safe_attach  重点注释

if ( ptrace(PTRACE_ATTACH, cid) < 0 )         // ptrace系统调用提供了一种方法,这个方法可以让一个进程监视
                                                // 控制另一个进程的执行,并且可以查看和更改被追踪进程的内存和寄存器。
                                                // 通常用来下断点和调试。
                                                // 子进程调用PTRACE_TRACEME,表明这个进程由它的父进程来跟踪
                                                // 任何发给这个进程的信号signal(除了SIGKILL)将导致该进程停止运行
                                                // 而它的父进程会通过wait()获得通知。另外,该进程之后所有对exec()的调用都将使操作系统产生一个SIGTRAP信号发送给它,这让父进程有机会在新程序开始执行之前获得对子进程的控制权。
                                                // 成功返回0。错误返回-1。errno被设置
    goto Fail_Code;                             // GOTO执行失败
  v8 = 0;                                       // 返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回
  while ( 1 )                                   // 循环作用主要是子进程退出时要干的事情
  {                                             // #define __WALL 0x40000000 等待所有类型的子进程,包括"clone"和"non-clone"
    result = waitpid(v3, &v8, 0x40000000);      // 如果在调用waitpid()函数时,当指定等待的子进程已经停止运行或结束了,则waitpid()会立即返回;但是如果子进程还没有停止运行或结束,则调用waitpid()函数的父进程则会被阻塞,暂停运行。
                                                // 这调试!
                                                // 如果不关心子进程为什么推出的话,也可以传入空指针
    if ( result != -1 )                         // 失败
      break;
    if ( *_errno(-1) != 4 )
      goto Fail_Code;
  }

handle_events 函数的分析

// //事件处理函数
__pid_t __fastcall handle_events(int a1)
{
  __pid_t result; // r0
  __pid_t v2; // r5
  const char *v3; // r0
  int v4; // r4
  signed int v5; // r3
  int v6; // [sp+0h] [bp-18h]
  int v7; // [sp+4h] [bp-14h]

  v6 = a1;                                      // 成功执行时,返回状态改变的子进程标识
  v7 = 0;
  do
  {                                             // 监控自己子进程
    while ( 1 )
    {
      result = waitpid(-1, &v7, 0x40000000);    // 参数-1 = 等待任一个子进程
      v2 = result;
      if ( result != -1 )
        break;
      if ( *_errno(-1) != 4 )
        goto LABEL_4;
    }
    if ( result < 0 )
    {
LABEL_4:
      v3 = "waitpid";
LABEL_13:
      perror(v3);
      exit(1);
    }
    if ( (v7 & 0x7F) == 127 && ((v7 + 1) & 0x7F) <= 1 )//     先来对wait status做个整体总结,一般我们通过wait status可以判定子进程发生了以下事件:
                                                //
                                                //    (1)子进程通过传递一个整形参数给exit(或者_exit)而正常退出
                                                //
                                                //    (2)子进程被一个信号终止
                                                //
                                                //    (3)子进程被一个信号暂停(调用waitpid时需指定WUNTRACED标志)
                                                //
                                                //    (4)暂停的子进程被信号SIGCONT恢复(调用waitpid时需指定WCONTINUED标志)
                                                //
    {
      v4 = v7 >> 8;
      v5 = may_cause_group_stop(v4) ? 0 : v4;
      result = ptrace(PTRACE_CONT, v2, 0, v5, v6);// 简单理解只要子程序被调试中断将被退出
      if ( result < 0 )
      {
        v3 = "PTRACE_CONT";
        goto LABEL_13;
      }
    }
  }
  while ( v7 << 25 && ((v7 + 1) & 0x7F) <= 1 ); // 成功执行时,返回状态改变的子进程标识
  return result;
}


分析完看到了SFLAG 数组,用于后面后面 check(JNIEnv *a1, int a2, char *getText) 做亦或运算用。
接着来看看check 函数

bool __fastcall check(JNIEnv *a1, int a2, char *getText)
{
  JNIEnv *v3; // r4
  char *strpass; // r7
  const void *v5; // r8
  int v6; // r2
  _BOOL4 v7; // r5
  char v9; // [sp+0h] [bp-38h]
  char v10; // [sp+1h] [bp-37h]
  char v11; // [sp+2h] [bp-36h]  //所以第四位是0
  char v12; // [sp+4h] [bp-34h]  //注意这里和上面少了一位
  char v13; // [sp+5h] [bp-33h]
  char v14; // [sp+6h] [bp-32h]
  char v15; // [sp+7h] [bp-31h]
  char v16; // [sp+8h] [bp-30h]
  char v17; // [sp+9h] [bp-2Fh]
  char v18; // [sp+Ah] [bp-2Eh]
  char v19; // [sp+Bh] [bp-2Dh]
  char v20; // [sp+Ch] [bp-2Ch]
  char v21; // [sp+Dh] [bp-2Bh]
  char v22; // [sp+Eh] [bp-2Ah]
  char v23; // [sp+Fh] [bp-29h] 最后一位是0

  v3 = a1;
  strpass = getText;
  if ( ((*a1)->GetStringLength)() != 16 )
    return 0;
  v5 = ((*v3)->GetStringUTFChars)(v3, strpass, 0);
  memset(&v9, 0, 0x11u);
  v12 = 1;
  v6 = 0;
  v19 = 0x11;
  v9 = 30;
  v10 = 29;
  v11 = 18;
  v13 = 18;
  v14 = 51;
  v15 = 11;
  v16 = 37;
  v17 = 120;
  v18 = 38;
  v20 = 64;
  v21 = 79;
  v22 = 74;
  v23 = 82;
  do
  {
    *(&v9 + v6) ^= sflag[v6];                   // FLAG是上面checkdebugger 函数动态生成的。
    ++v6;
  }
  while ( v6 != 16 );
  v7 = memcmp(v5, &v9, 16u) == 0;               // 爆破关键位置
  ((*v3)->ReleaseStringUTFChars)(v3, strpass, v5);// 释放
  return v7;
}

这里注意,就是V9 字符串的问题,上面我注释了。第四位和最后一位。


开VS2017

int main()
{
        char sflag[] = { "F8LEFT" };
        char str[] = { 30,29,18,0,1,18,51,11,37,120,38,17,64,79,74,82,0 };
        
        char flag[] = { 74,117,115,116,72,97,118,101,65,84,114,121,33,33,33,33 };
        char str2[17] = { 0 };
        for (size_t i = 0; i < 16; i++)
        {
                str2= str ^ flag;
        }

        cout << str2 << endl;

cm2.JPG
最终密码=ThatIsEnd,Thanks

免费评分

参与人数 5威望 +1 吾爱币 +12 热心值 +4 收起 理由
qtfreet00 + 1 + 9 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
封帆帆 + 1 我很赞同!
劣酒先生 + 1 + 1 热心回复!
fei8255 + 1 + 1 用心讨论,共获提升!
耳食之辈 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| shuaiyue 发表于 2019-4-24 17:40
pk8900 发表于 2019-4-24 07:45
for (size_t i = 0; i < 16; i++)
        {
                str2= str ^ flag;

估计是缓存造成的,编辑里都是正确的,显示就是错的。 str2=str ^ flag
pk8900 发表于 2019-4-24 18:33
本帖最后由 pk8900 于 2019-4-24 18:35 编辑
shuaiyue 发表于 2019-4-24 17:40
估计是缓存造成的,编辑里都是正确的,显示就是错的。 str2=str ^ flag
str2[i]= str[i] ^ flag[i];

用插入代码功能就好了,你分析的没错,我研究了一下,确实是这个flag
wang19940311 发表于 2019-4-24 06:19
yuedingc 发表于 2019-4-24 07:38
没有看懂是什么
pk8900 发表于 2019-4-24 07:45
for (size_t i = 0; i < 16; i++)
        {
                str2= str ^ flag;
        }
能行吗?
zcgts 发表于 2019-4-24 11:06
看不懂呀,我的天
 楼主| shuaiyue 发表于 2019-4-24 19:46

谢谢提醒,没注意看还有这功能。
血色天空 发表于 2019-4-24 20:13
想楼主学习,高手
cdevil 发表于 2019-4-24 20:37
太厉害了,向楼主学习
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 22:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表