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;
}
动调发现调不了,无语了,看了下汇编,ida又优化了,讨厌ida.qword_14001E1F0的值为0,不是很理解,因为这里是错的,我都没看出为什么,但是z佬说这是符号修饰.
导出表有个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

内存异常访问

点击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知识大问答
还好
罗师傅的图片(忘了题目名字)
百度识图可得图片地方在客运中心站

浙公网安备 33010602011771号