Hgame-final复盘

RE

Crackme

获得name为"hgame"的许可证

__int64 __fastcall sub_1400123F0(HINSTANCE a1)
{
  char *v1; // rdi
  __int64 i; // rcx
  char v4[32]; // [rsp+0h] [rbp-60h] BYREF
  char v5; // [rsp+60h] [rbp+0h] BYREF
  WNDCLASSW WndClass; // [rsp+70h] [rbp+10h] BYREF
  HWND hWnd; // [rsp+D8h] [rbp+78h]
  struct tagMSG Msg; // [rsp+F8h] [rbp+98h] BYREF

  v1 = &v5;
  for ( i = 58i64; i; --i )
  {
    *(_DWORD *)v1 = -858993460;
    v1 += 4;
  }
  sub_1400113D9(&unk_1400240AD);
  WndClass.lpfnWndProc = (WNDPROC)sub_140011230;
  WndClass.hInstance = a1;
  WndClass.lpszClassName = L"Crackme";
  WndClass.hbrBackground = (HBRUSH)GetStockObject(0);
  WndClass.hCursor = LoadCursorW(0i64, (LPCWSTR)0x7F00);
  WndClass.hIcon = LoadIconW(0i64, (LPCWSTR)0x7F00);
  WndClass.lpszMenuName = 0i64;
  WndClass.style = 3;
  WndClass.cbClsExtra = 0;
  WndClass.cbWndExtra = 0;
  RegisterClassW(&WndClass);
  hWnd = CreateWindowExW(0, L"Crackme", L"Crackme", 0xCC0000u, 0, 0, 800, 600, 0i64, 0i64, a1, 0i64);
  ShowWindow(hWnd, 1);
  UpdateWindow(hWnd);
  while ( GetMessageW(&Msg, 0i64, 0, 0) )
  {
    TranslateMessage(&Msg);
    DispatchMessageW(&Msg);
  }
  return sub_140011366(v4, &unk_14001AD80);
}

注册窗口,不管这些,直接看主函数sub_140011230逻辑:

LRESULT __fastcall sub_140011B10(HWND a1, UINT a2, WPARAM a3, LPARAM a4)
{
  char *v4; // rdi
  __int64 i; // rcx
  DWORD v6; // eax
  DWORD v7; // eax
  LRESULT v8; // rax
  LRESULT v9; // rdi
  char v11[32]; // [rsp+0h] [rbp-60h] BYREF
  char v12; // [rsp+60h] [rbp+0h] BYREF
  struct tagRECT Rect; // [rsp+68h] [rbp+8h] BYREF
  int nWidth; // [rsp+94h] [rbp+34h]
  int nHeight; // [rsp+B4h] [rbp+54h]
  int v16; // [rsp+D4h] [rbp+74h]
  int v17; // [rsp+F4h] [rbp+94h]
  PVOID Block; // [rsp+118h] [rbp+B8h]
  PVOID pvData; // [rsp+138h] [rbp+D8h]
  LPARAM v20; // [rsp+158h] [rbp+F8h]
  int v21; // [rsp+174h] [rbp+114h]
  int v22; // [rsp+194h] [rbp+134h]
  int X; // [rsp+1B4h] [rbp+154h]
  int Y; // [rsp+1D4h] [rbp+174h]
  int v25; // [rsp+1F4h] [rbp+194h]
  int v26; // [rsp+214h] [rbp+1B4h]
  int v27; // [rsp+234h] [rbp+1D4h]
  LSTATUS ValueA; // [rsp+254h] [rbp+1F4h]
  DWORD pdwType[8]; // [rsp+274h] [rbp+214h] BYREF
  DWORD pcbData[8]; // [rsp+294h] [rbp+234h] BYREF
  __int16 v31; // [rsp+2B4h] [rbp+254h]
  __int16 v32; // [rsp+2D4h] [rbp+274h]
  LPARAM v33; // [rsp+2F8h] [rbp+298h]
  CHAR Str[56]; // [rsp+318h] [rbp+2B8h] BYREF
  CHAR String[516]; // [rsp+350h] [rbp+2F0h] BYREF
  UINT v36; // [rsp+554h] [rbp+4F4h]

  v4 = &v12;
  for ( i = 226i64; i; --i )
  {
    *(_DWORD *)v4 = -858993460;
    v4 += 4;
  }
  sub_1400113D9(&unk_1400240AD);
  nWidth = 100;
  nHeight = 30;
  v16 = 550;
  v17 = 30;
  Block = 0i64;
  pvData = 0i64;
  v36 = a2;
  if ( a2 == 1 )
  {
    v20 = a4;
    GetClientRect(a1, &Rect);
    v21 = Rect.right - Rect.left;
    v22 = Rect.bottom - Rect.top;
    X = (Rect.right - Rect.left - nWidth) / 2;
    Y = 2 * ((Rect.bottom - Rect.top - nHeight) / 3);
    v25 = (Rect.right - Rect.left - v16) / 2;
    v26 = Y - 50;
    v27 = Y - 100;
    pcbData[0] = 0;
    ValueA = RegGetValueA(HKEY_CURRENT_USER, SubKey, "Name", 1u, pdwType, 0i64, pcbData);
    if ( !ValueA )
    {
      Block = malloc(pcbData[0]);
      j_memset(Block, 0, pcbData[0]);
      ValueA = RegGetValueA(HKEY_CURRENT_USER, SubKey, "Name", 1u, pdwType, Block, pcbData);
    }
    ValueA = RegGetValueA(HKEY_CURRENT_USER, SubKey, "License", 1u, pdwType, 0i64, pcbData);
    if ( !ValueA )
    {
      pvData = malloc(pcbData[0]);
      j_memset(pvData, 0, pcbData[0]);
      ValueA = RegGetValueA(HKEY_CURRENT_USER, SubKey, "License", 1u, pdwType, pvData, pcbData);
    }
    if ( !Block || !pvData )
    {
      Block = malloc(5ui64);
      j_memset(Block, 0, 5ui64);
      pvData = malloc(8ui64);
      j_memset(pvData, 0, 8ui64);
      strncpy((char *)Block, "Name", 4ui64);
      strncpy((char *)pvData, "License", 7ui64);
    }
    qword_14001E208 = (__int64)CreateWindowExW(
                                 0,
                                 L"button",
                                 L"Cilck",
                                 0x50000000u,
                                 X,
                                 Y,
                                 nWidth,
                                 nHeight,
                                 a1,
                                 (HMENU)0x3E8,
                                 *(HINSTANCE *)(v20 + 8),
                                 0i64);
    hWnd = CreateWindowExA(
             0,
             "edit",
             (LPCSTR)pvData,
             0x50800000u,
             v25,
             v26,
             v16,
             v17,
             a1,
             (HMENU)0x3E9,
             *(HINSTANCE *)(v20 + 8),
             0i64);
    qword_14001E218 = CreateWindowExA(
                        0,
                        "edit",
                        (LPCSTR)Block,
                        0x50800000u,
                        v25,
                        v27,
                        v16,
                        v17,
                        a1,
                        (HMENU)0x3EA,
                        *(HINSTANCE *)(v20 + 8),
                        0i64);
    goto LABEL_24;
  }
  if ( v36 == 2 )
  {
    free(Block);
    free(pvData);
    PostQuitMessage(0);
LABEL_24:
    v8 = 0i64;
    goto LABEL_25;
  }
  if ( v36 == 273 )
  {
    v32 = WORD1(a3);
    v31 = a3;
    v33 = a4;
    if ( (unsigned __int16)a3 == 1000 && !v32 && hWnd && qword_14001E218 )
    {
      GetWindowTextA(hWnd, String, 70);
      GetWindowTextA(qword_14001E218, Str, 8);
      if ( (unsigned int)qword_14001E1F0(Str, String) )
      {
        v6 = j_strlen(Str);
        RegSetKeyValueA(HKEY_CURRENT_USER, SubKey, "Name", 1u, Str, v6);
        v7 = j_strlen(String);
        RegSetKeyValueA(HKEY_CURRENT_USER, SubKey, "License", 1u, String, v7);
        MessageBoxA(a1, "Success!", "Crackme", 0);
      }
      else
      {
        MessageBoxA(a1, "Failed!", "Crackme", 0);
      }
    }
    goto LABEL_24;
  }
  v8 = DefWindowProcW(a1, a2, a3, a4);
LABEL_25:
  v9 = v8;
  sub_140011366(v11, &unk_14001AF20);
  return v9;
}

主要部分是:

if ( (unsigned __int16)a3 == 1000 && !v32 && hWnd && qword_14001E218 )
    {
      GetWindowTextA(hWnd, String, 70);
      GetWindowTextA(qword_14001E218, Str, 8);
      if ( (unsigned int)qword_14001E1F0(Str, String) )
      {
        v6 = j_strlen(Str);
        RegSetKeyValueA(HKEY_CURRENT_USER, SubKey, "Name", 1u, Str, v6);
        v7 = j_strlen(String);
        RegSetKeyValueA(HKEY_CURRENT_USER, SubKey, "License", 1u, String, v7);
        MessageBoxA(a1, "Success!", "Crackme", 0);
      }
      else
      {
        MessageBoxA(a1, "Failed!", "Crackme", 0);
      }
    }

这里读取了传入文本进行校验,我们交叉应用看一下qword_14001E1F0(Str, String)

发现有个加载库函数:

HMODULE sub_1400118A0()
{
  HMODULE result; // rax

  sub_1400113D9(&unk_1400240AD);
  result = LoadLibraryA(LibFileName);
  hModule = result;
  if ( result )
  {
    result = (HMODULE)GetProcAddress(hModule, "Check");
    qword_14001E1F0 = (__int64 (__fastcall *)(_QWORD, _QWORD))result;
  }
  return result;
}

去看看提供的nobody.dll文件查看Check函数:

_BOOL8 __fastcall Check(char *name, char *license)
{
  __int64 hash; // [rsp+48h] [rbp+28h]
  char *true_license; // [rsp+68h] [rbp+48h]

  sub_180011357(&unk_1800240A3);
  j_strlen(name);

  hash = SHA256(name);
  xtea(hash);
  true_license = (char *)format(hash);
  return strncmp(true_license, license, 0x40ui64) == 0;
}

动调发现调不了,qword_14001E1F0的值为0,不是很理解,因为这里是错的,我都没看出为什么,但是z佬说这是符号修饰.无语了,看了下汇编,ida又优化了,讨厌ida.

导出表有个tls回调看一下,发现调用RemoveVectoredExceptionHandlerapi,说明有注册veh异常调用.

veh是进程的异常调用,seh是线程的异常调用,捕获异常的优先级为:调试器->VEH->SEH(这里导致我不知道为什么跳转不了)

导入表用注册,交叉应用看一下.

PVOID sub_140011920()
{
  PVOID result; // rax

  sub_1400113D9(&unk_1400240AD);
  result = AddVectoredExceptionHandler(1u, Handler);
  Handle = result;
  return result;
}

handler:

__int64 __fastcall Handler_0(__int64 a1)
{
  char *v1; // rdi
  __int64 i; // rcx
  char v4[32]; // [rsp+0h] [rbp-20h] BYREF
  char v5[304]; // [rsp+20h] [rbp+0h] BYREF

  v1 = v5;
  for ( i = 26i64; i; --i )
  {
    *(_DWORD *)v1 = -858993460;
    v1 += 4;
  }
  sub_1400113D9(&unk_1400240AD);
  memset(&v5[16], 0, 0x40ui64);
  if ( **(_DWORD **)a1 == -1073741819 )
  {
    if ( *(_QWORD *)(*(_QWORD *)(a1 + 8) + 248i64) || (*(_DWORD *)(*(_QWORD *)(a1 + 8) + 68i64) & 0x100) != 0 )
    {
      RemoveVectoredExceptionHandler(Handle);
      MessageBoxA(0i64, "Error", "Crackme", 0);
      exit(0);
    }
    *(_QWORD *)(*(_QWORD *)(a1 + 8) + 248i64) = hModule + 18100;
  }
  sub_140011366(v4, &unk_14001AFD0);
  return 0xFFFFFFFFi64;
}

这里是捕获内存异常访问的报错,读取了上下文,查看有无被硬件断点和单步调试.

没有就把rip修改

值得注意一点:这里18100是int型加上18100,什么意思呢?

int a[10] = {0};
int *pointer = a;
pointer++;

对于int指针的pointer这里是加1,但是对于内存它加了32位.

所以我们要去看汇编,得到11AD0h,这才是内存里的偏移值.

我们动调,输入hgame

image-20250317102152084

内存异常访问

image-20250317102341490

点击yes.

调到相应内存位置,c一下,p一下:

__int64 __fastcall sub_7FFF3D881AD0(__int64 a1, __int64 a2)
{
  char *v2; // rdi
  __int64 i; // rcx
  __int64 v4; // rcx
  __int64 v5; // rax
  __int64 v6; // rdi
  char v8[32]; // [rsp+0h] [rbp-20h] BYREF
  char v9[4]; // [rsp+20h] [rbp+0h] BYREF
  int v10; // [rsp+24h] [rbp+4h]
  __int64 v11; // [rsp+48h] [rbp+28h]
  int v12[12]; // [rsp+68h] [rbp+48h] BYREF
  __int64 v13; // [rsp+98h] [rbp+78h]
  int j; // [rsp+B4h] [rbp+94h]
  unsigned __int64 v15; // [rsp+188h] [rbp+168h]

  v2 = v9;
  for ( i = 46i64; i; --i )
  {
    *(_DWORD *)v2 = -858993460;
    v2 += 4;
  }
  v15 = (unsigned __int64)v9 ^ qword_7FFF3D88F000;
  ((void (__fastcall *)(void *))unk_7FFF3D881357)(&unk_7FFF3D8940A3);
  v10 = ((__int64 (__fastcall *)(__int64))unk_7FFF3D881186)(a1);
  qmemcpy(v12, "Seven123456Seven", 16);
  v11 = ((__int64 (__fastcall *)(__int64))unk_7FFF3D881159)(a1);
  LOBYTE(v4) = 32;
  ((void (__fastcall *)(__int64, int *, __int64, __int64))unk_7FFF3D881078)(v4, v12, v11, v11);
  v13 = ((__int64 (__fastcall *)(__int64))unk_7FFF3D881104)(v11);
  for ( j = 0; j < 64; ++j )
  {
    if ( *(char *)(v13 + j) != *(char *)(a2 + j) )
    {
      v5 = 0i64;
      goto LABEL_10;
    }
  }
  v5 = 1i64;
LABEL_10:
  v6 = v5;
  ((void (__fastcall *)(char *, void *))unk_7FFF3D8812F3)(v8, &unk_7FFF3D88B950);
  return v6;
}

直接上官方wp:

第一次加密,分析一下知道这是SHA256

第二次是key 为Seven123456Seven的SM4 ECB加密

没换表 常量直接搜

可以直接动调dump出来.

battler

不多写,其实确实简单,但是我当时又困又急,脚本写错没时间改了.

其实程序的逻辑还是清楚的,哪怕用一堆abcd来代替,哪怕activity不写清楚.

直接搜索score,能发现一个f9635g,猜测是score:

public void e() {
        M.a(0.15f, 0.15f, 0.2f, 1.0f);
        if (!this.f9644p) {
            this.f9629a.k();
            this.f9629a.y(this.f9632d, this.f9633e - 400.0f, this.f9634f - 100.0f);
            this.f9640l.b(this.f9629a);
            this.f9641m.b(this.f9629a);
            i();
            this.f9630b.draw(this.f9629a, "Score: " + this.f9635g, 100.0f, this.f9634f - 50.0f);
            this.f9630b.draw(this.f9629a, "Health: " + this.f9636h.f9669y, 100.0f, this.f9634f - 150.0f);
            this.f9629a.e();
        }

交叉应用继续跟:

public void a(final d dVar) {
        int i2;
        dVar.f9644p = true;
        AndroidLauncher androidLauncher = this.f9724a;
        GameService gameService = androidLauncher.f7731w;
        gameService.f7735b = dVar.f9635g;
        androidLauncher.startService(androidLauncher.f7730v);
        do {
            i2 = gameService.f7736c;
        } while (i2 == 0);
        String str = "游戏结束\n";
        if (i2 == 1) {
            str = "游戏结束\n菜就多练!!!";
        } else if (i2 == 2) {
            str = "游戏结束\n恭喜你,通过了考验!!My Battler";
        } else if (i2 == 3) {
            str = "游戏结束\n我早就发现你了,作弊者";
        }

package com.nobody.battle.android;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

/* loaded from: classes.dex */
public class GameService extends Service {

    /* renamed from: a, reason: collision with root package name */
    private final IBinder f7734a = new a();

    /* renamed from: b, reason: collision with root package name */
    public int f7735b = 0;

    /* renamed from: c, reason: collision with root package name */
    public int f7736c = 0;

    public class a extends Binder {
        public a() {
        }

        GameService a() {
            return GameService.this;
        }
    }

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

    static native int aaaa(int i2);

    @Override // android.app.Service
    public IBinder onBind(Intent intent) {
        return this.f7734a;
    }

    @Override // android.app.Service
    public void onDestroy() {
        super.onDestroy();
    }

    @Override // android.app.Service
    public int onStartCommand(Intent intent, int i2, int i3) {
        this.f7736c = aaaa(this.f7735b);
        return super.onStartCommand(intent, i2, i3);
    }
}

有个本地函数aaaa,去so看看,是个被ollvm混淆的xxtea,看的很烦,然后脚本写错了,又不想改,也没时间了,就没做了.

其实可以用frida,但是我frida学的不多,hook不来,而且我看后续log_print也要hook(不知道,官方wp说的)

先粘贴一下官方hook脚本:

function hookTest() {
  let GameService = Java.use("com.nobody.battle.android.GameService");
  GameService["aaaa"].implementation = function (i2) {
    console.log(`GameService.aaaa is called: i2=${i2}`);
    let result = this["aaaa"](1000000);
    console.log(`GameService.aaaa result=${result}`);
    return result;
  };
  var liblog = Module.findBaseAddress("liblog.so");
  if (liblog) {
    // 查找 __android_log_print 函数的地址
    var logPrint = Module.findExportByName("liblog.so", "__android_log_print");
    if (logPrint) {
      // 对 __android_log_print 函数进行 Hook
      Interceptor.attach(logPrint, {
        // 在函数调用前执行的逻辑
        onEnter: function (args) {
          // args[0] 是日志级别,args[1] 是日志标签,args[2] 是日志格式字符串
          var priority = args[0].toInt32();
          var tag = Memory.readUtf8String(args[1]);
          var format = Memory.readUtf8String(args[2]);
          var message = Memory.readUtf8String(args[3]);
          // 在 Hook 之前输出函数调用前的日志信息
          if (tag == "Native") {
            console.log(
              `__android_log_print(${priority}, "${tag}", "${format}", "${message}")`
            );
          }
        },
        // 在函数调用后执行的逻辑
        onLeave: function (retval) {
          console.log(
            "After __android_log_print, return value: " + retval.toInt32()
          );
        },
      });
    } else {
      console.log("__android_log_print not found in liblog.so");
    }
  } else {
    console.log("liblog.so not found");
  }
}
function main() {
  Java.perform(function () {
    hookTest();
  });
}
setImmediate(main);

babyre

用那些沟槽的128位符号写的模拟rc4,ai一把梭.

不过我自己看不出来()

misc

vidar知识大问答

还好

罗师傅的图片(忘了题目名字)

百度识图可得图片地方在客运中心站pictrue

posted @ 2025-04-16 19:24  T0fV404  阅读(30)  评论(0)    收藏  举报