存档

作者存档

hf/war3辅助指南

2014年12月14日 没有评论

war3版本1.20e,hf是5.8某不知名优化版。

anti-war3自身保护:
修改game.dll:偏移量0000995D,修改长度:1字节,原始值:74,新值:EB
T人:

hf画中画阉割:%hfgame%\htmlayout.dll,用一个空文件覆盖。可能也有屏蔽小喇叭之功效。
如果不需要hf的改键,可以阉割%hfgame%\plugin\GameClient\tigerbkey.dll,同样是用空文件覆盖。需要注意的是,hf的画中画会覆盖war3的窗口,如果没阉割hf的画中画的话,第三方改键可能不能生效(因为检测不到war3窗口)。
退出时广告:删掉文件夹%hfgame%\config\PopNotice并创建只读的同名空文件。

GGWar3.dll好像是算积分类游戏用的,如果不玩类似的只玩玩TD,可以用一个空DLL覆盖,可以大幅提高war3启动速度。
覆盖GGWar3.dll的不能是空文件,得是一个无功能的合法DLL,否则没法进入hf的游戏房间。实在找不到合法DLL的话就从C:\Windows\System32(64位系统是SysWOW64)下面随便找一个小点的DLL也可以。

vxxxxf 系列函数会修改 args

2014年4月7日 2 条评论

之前碰到过一次,结果 de 完 bug 转头就忘了。
于是今天就又花了半天来 de 这个 bug。

vxxxxf 包括 vsnprintf 等等等等。

C99 / C++11 有一个新的宏 va_copy,可以制造一个 args 的副本,然后每次调用 vxxxxf 之前传副本进去就好。
ref: http://en.cppreference.com/w/cpp/utility/variadic/va_copy

一个例子(把 vsnprintf 通过返回值返回错误改成了通过异常指示错误):

size_t string_printf(
    char *buffer,
    size_t buffer_size,
    const char *format,
    va_list args
    )
{
    
    //
    // args 第一次用于 vsnprintf 计算所需要的字符数,由于 vsnprintf
    // 可能会修改 args(实际上确实修改了),所以要搞一个副本给第一次
    // 的 vsnprintf 做参数。
    //

    va_list args_copy;

    va_copy(args_copy, args);

    BOOST_SCOPE_EXIT(&args_copy)
    {
        va_end(args_copy);
    }
    BOOST_SCOPE_EXIT_END;

    int cch_needed = vsnprintf(nullptr, 0, format, args_copy);

    if (cch_needed < 0)
    {
        throw std::runtime_error("vsnprintf failed.");
    }
    else if (static_cast<size_t>(cch_needed) > buffer_size - 1)
    {
        throw std::overflow_error("Buffer is too small.");
    }

    cch_needed = vsnprintf(buffer, buffer_size, format, args);

    if (cch_needed < 0 || static_cast<size_t>(cch_needed) > buffer_size - 1)
    {
        throw std::runtime_error("Unexpected exception.");
    }

    return cch_needed;
}

Windows Phone 8 连接不上电脑 Wifi 的问题

2013年4月7日 2 条评论

rt,会提示“您的手机无法连接 WLAN 网络”。

搞了半天,结论是,把无线网卡的 802.11n 关掉就行了。也就是说,让电脑开出来的 Wifi 是 802.11g 而不是 802.11n 的。

应该是 Windows Phone 8 自带的驱动有问题,记得很早以前也在别人的安卓机上见过类似的情况。

不知不觉,又过了一个秋

2013年2月18日 4 条评论

Time flies.

为 putty 增加 CP936 / GBK 支持

2012年11月19日 2 条评论

其实就算增加了 CP936 之后,问题还是挺多……各种删除半个汉字。

文件 window.c

函数 static void init_fonts(int pick_width, int pick_height):
注释掉:

  GetCPInfo(ucsdata.font_codepage, &cpinfo);
  ucsdata.dbcs_screenfont = (cpinfo.MaxCharSize > 1);

文件 winucs.c

数组 static const struct cp_list_item cp_list[]:
增加:
{"CP936", 936},

函数:void init_ucs(Config *cfg, struct unicode_data *ucsdata):
在代码段:

    /* Decide on the Line and Font codepages */
    ucsdata->line_codepage = decode_codepage(cfg->line_codepage);
之后插入:
  if (ucsdata->line_codepage > 0) {
    ucsdata->font_codepage = ucsdata->line_codepage;
  }
  
  {
    CPINFO cpinfo;
    GetCPInfo(ucsdata->font_codepage, &cpinfo);
    ucsdata->dbcs_screenfont = (cpinfo.MaxCharSize > 1);
  }

魔兽争霸 3 踢人内挂

2012年10月27日 4 条评论

话说 war3 会自动把 war3.exe 所在目录下的 .mix 文件作为 DLL 加载~~

所以编译成 DLL 改成 xxx.mix 放在 war3.exe 的目录里面就好了~~

Update at 0:23 2012/10/28: 我忽然发现我几乎把所有的错误处理都忽略掉了。大概是太久没写代码了。

//
// 默认调用约定:__stdcall
//
// WAR3 数据包格式来源于 W3GStreamer。
//

#include <Windows.h>
#include <WinSock.h>

#define ORDINAL_SEND (0x00000013)

#define ENTRY_NOT_FOUND (-1)

#define PACKET_SIGNATURE (0xF7)
#define PACKET_CHAT (0x0F)

#define PACKET_CHAT_FLAG_IN_PREPARE_SCREEN (0x10)
#define PACKET_CHAT_FLAG_IN_GAME (0x20)

#define COMMAND_KICK "/kick"

#define Add2Ptr(Pointer, Increment, ResultType) (reinterpret_cast<ResultType>((ULONG_PTR)(Pointer) + (Increment)))
#define PtrOffset(Pointer2, Pointer1) ((reinterpret_cast<ULONG_PTR>(Pointer2) - reinterpret_cast<ULONG_PTR>(Pointer1)))

typedef DWORD (SEND) (
  __in SOCKET Socket,
  __in_bcount(BufferSize) PVOID Buffer,
  __in DWORD BufferSize,
  __in DWORD Flags
  );

typedef SEND *PSEND;

typedef __declspec(align(1)) struct _GAME_PACKET {
  BYTE Signature;
  BYTE Operation;
  WORD PacketSize;

  union {
    struct {
      BYTE TargetCount;
      
      //
      // 接下来是目标 ID。
      //
      // BYTE Id[TargetCount];
      //

      BYTE SelfId;
      BYTE Flags;
      
      //
      // 如果 Flags 是 PACKET_CHAT_FLAG_IN_GAME,那么接下来会有 Target。
      //
      // DWORD Target;
      //

      CHAR Message[1];
    } Chat;
  };
} GAME_PACKET, *PGAME_PACKET;

PSEND OldSend;

//
// 定位映像数据目录。
//

PVOID LocateImageDataDirectory(
  __in PVOID ImageBase,
  __in ULONG DataDirectory
  )
{
  PIMAGE_DOS_HEADER DosHeader;
  PIMAGE_NT_HEADERS NtHeaders;

  DosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(ImageBase);
  NtHeaders = Add2Ptr(ImageBase, DosHeader->e_lfanew, PIMAGE_NT_HEADERS);

  //
  // 从文件头中定位数据目录。
  //

  return Add2Ptr(ImageBase, NtHeaders->OptionalHeader.DataDirectory[DataDirectory].VirtualAddress, PVOID);
}

//
// 根据 DLL 文件名定位导入描述符。
//

PIMAGE_IMPORT_DESCRIPTOR LocateImportDescriptor(
  __in PVOID ImageBase,
  __in PSTR DllName
  )
{
  PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;

  //
  // 定位导入描述符。
  //

  ImportDescriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(LocateImageDataDirectory(ImageBase, 
    IMAGE_DIRECTORY_ENTRY_IMPORT));

  while (ImportDescriptor->Name) {
    PSTR ImportedDllName;

    //
    // 比较 DLL 文件名。
    //

    ImportedDllName = Add2Ptr(ImageBase, ImportDescriptor->Name, PSTR);
    
    if (!_strcmpi(DllName, ImportedDllName)) {
      return ImportDescriptor;
    }

    ImportDescriptor++;
  }

  return NULL;
}

//
// 定位导入查找表。
//

PULONG_PTR LocateImportEntries(
  __in PVOID ImageBase,
  __in PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor,
  __in BOOL LocateOriginalEntries
  )
{
  if (LocateOriginalEntries) {
    return Add2Ptr(ImageBase, ImportDescriptor->OriginalFirstThunk, PULONG_PTR);
  } else {
    return Add2Ptr(ImageBase, ImportDescriptor->FirstThunk, PULONG_PTR);
  }
}

//
// 比较导入查找表中的表项。
//
// 这个函数可以正确处理按照序号导入的表项。
//

BOOL CompareImportEntry(
  __in PVOID ImageBase,
  __in ULONG_PTR ImportedEntry,
  __in PSTR EntryToLookup
  )
{
  if (IMAGE_SNAP_BY_ORDINAL(ImportedEntry) != 
    IMAGE_SNAP_BY_ORDINAL(reinterpret_cast<ULONG_PTR>(EntryToLookup))) {
    return FALSE;
  }
  
  if (IMAGE_SNAP_BY_ORDINAL(ImportedEntry)) {

    //
    // ImportedEntry 是一个函数的序号。也意味着 EntryToLookup 也是一个序号。
    //

    return IMAGE_ORDINAL(ImportedEntry) == IMAGE_ORDINAL(reinterpret_cast<ULONG_PTR>(EntryToLookup));
  } else {

    //
    // 比较字符串。
    //

    return !strcmp(Add2Ptr(ImageBase, ImportedEntry, PSTR), EntryToLookup);
  }
}

//
// 查找导入查找表表项。
//
// 如果 ProcName 是一个序号,那么它的最高位需要被设为 1。
//

DWORD LookupImportEntryIndex(
  __in PVOID ImageBase,
  __in PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor,
  __in PSTR ProcName
  )
{
  DWORD Index;
  PULONG_PTR Entries;

  Index = 0;

  //
  // 定位导入查找表。
  //

  Entries = LocateImportEntries(ImageBase, ImportDescriptor, TRUE);

  //
  // 遍历导入查找表。
  //
  
  while (ULONG_PTR Offset = Entries[Index]) {

    //
    // 比较函数名 / 序号。
    //

    if (CompareImportEntry(ImageBase, Offset, ProcName)) {
      return Index;
    }

    Index++;
  }

  return ENTRY_NOT_FOUND;
}

//
// IAT HOOK。
//

PVOID InterceptImportedProc(
  __in PTSTR TargetName,
  __in PSTR DllName,
  __in PSTR ProcName,
  __in PVOID HookProc
  )
{
  PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
  DWORD ImportEntryIndex;
  PULONG_PTR Entries;
  PVOID TargetBase;

  //
  // 定位要 HOOK 的模块。
  //

  TargetBase = reinterpret_cast<PVOID>(GetModuleHandle(TargetName));

  //
  // 定位导入描述符。
  //

  ImportDescriptor = LocateImportDescriptor(TargetBase, DllName);

  //
  // 查找导入的函数的索引。
  //

  ImportEntryIndex = LookupImportEntryIndex(TargetBase, ImportDescriptor, ProcName);

  //
  // 定位导入函数的地址表。
  //

  Entries = LocateImportEntries(TargetBase, ImportDescriptor, FALSE);

  //
  // 读取当前地址并替换。
  //

  return reinterpret_cast<PVOID>(InterlockedExchange(reinterpret_cast<volatile ULONG_PTR *>(&Entries[ImportEntryIndex]),
    reinterpret_cast<ULONG_PTR>(HookProc)));
}

//
// WSASend 的过滤函数。
//

DWORD NewSend(
  __in SOCKET Socket,
  __in_bcount(BufferSize) PVOID Buffer,
  __in DWORD BufferSize,
  __in DWORD Flags
    )
{
  PGAME_PACKET GamePacket, NewGamePacket;

  GamePacket = reinterpret_cast<PGAME_PACKET>(Buffer);

  //
  // 检测是否为发送消息的包。
  //

  if (GamePacket->Signature == PACKET_SIGNATURE && GamePacket->Operation == PACKET_CHAT) {
    PSTR MessageBuffer;
    
    //
    // 标志位之前有 TargetCount 个 ID,每个 ID 占用一个 BYTE 的大小。
    //

    NewGamePacket = Add2Ptr(GamePacket, GamePacket->Chat.TargetCount * sizeof(BYTE), PGAME_PACKET);

    if (NewGamePacket->Chat.Flags == PACKET_CHAT_FLAG_IN_GAME) {

      //
      // 如果 Flags == PACKET_CHAT_FLAG_IN_GAME,那么在消息正文之前还有一个 Target,占用一个 DWORD。
      //

      NewGamePacket = Add2Ptr(NewGamePacket, sizeof(DWORD), PGAME_PACKET);
    }

    MessageBuffer = NewGamePacket->Chat.Message;

    //
    // 比较命令。(MessageBuffer 是以 0 结尾的。)
    //

    if (!strcmp(MessageBuffer, COMMAND_KICK)) {

      //
      // 返回错误。游戏会自动断开连接。
      //

      return SOCKET_ERROR;
    }
  }

  return OldSend(Socket, Buffer, BufferSize, Flags);
}

//
// DLL 入口点。
//

BOOL DllMain(
  __in HINSTANCE DllBase,
  __in DWORD Reason,
  __reserved PVOID Reserved
  )
{
  if (Reason == DLL_PROCESS_ATTACH) {

    //
    // HOOK。
    //

    OldSend = reinterpret_cast<PSEND>(InterceptImportedProc(TEXT("war3.exe"), "wsock32.dll", 
      reinterpret_cast<PSTR>(ORDINAL_SEND | IMAGE_ORDINAL_FLAG), reinterpret_cast<PVOID>(NewSend)));

  } else if (Reason == DLL_PROCESS_DETACH) {

    //
    // UNHOOK。
    //

    InterceptImportedProc(TEXT("war3.exe"), "wsock32.dll", 
      reinterpret_cast<PSTR>(ORDINAL_SEND | IMAGE_ORDINAL_FLAG), reinterpret_cast<PVOID>(OldSend));
  }

  return TRUE;
}

重建傲游3的图标缓存

2012年9月4日 没有评论

确切来说应该叫做删除。

删除 Maxthon/UserData/Public/SiteIcon/SiteIcon.dat 即可。

注:这是傲游3便携版的地址,至于安装版是不是这个路径,我估计很可能不是了。

屏蔽使用360浏览器及其搜索引擎的用户的访问

2012年9月3日 3 条评论

不懂JS跟PHP,代码写的渣。勿喷。

请将其中的“[需要转到的网址]”替换为检测到360后需要转向的实际网址(如“http://www.rising.com.cn/”)。
因为流量有限,所以各位就别转到我这儿了。

因为所谓的“360搜索”(我始终不认为一个靠抓取别人结果做搜索的“搜索”可以称为搜索)的蜘蛛的UA是“360Spider”,所以也会被下面的php识别。因此下面的PHP版本也可以让360的蜘蛛抓的是被转到的页面,而不是实际的页面。

JS版本比PHP版本多了一种检测方式,不过只能检测360“安全”浏览器,不能检测360极速浏览器。

PHP版本,可以放在 WordPress 的“外观 – 编辑 – 模板函数 (functions.php)”中:

call_user_func(function() {
  function match_string_any($text, $patterns) {
    foreach ($patterns as $current) {
      if (stripos($text, $current) !== false) {
        return true;
      }
    }
    
    return false;
  };
  
  $criminal_refs = array('.360.', '.360safe.', '.360so.', '.360sou.', '.so.', '.360webcache.');
  $criminal_uas = array('360');
  
  if (match_string_any($_SERVER['HTTP_USER_AGENT'], $criminal_uas) ||
    match_string_any($_SERVER["HTTP_REFERER"], $criminal_refs)) {
    die(file_get_contents("[需要转到的网址]"));
  }
});

JS版本,可以放在 WordPress 的“外观 – 编辑 – 顶部 (templates/header.php)”中:

<script type="text/javascript">

function crimeDetected() {
  document.location.replace("[需要转到的网址]");
}
  
(function () {
  function matchStringAny(text, patterns) {
    if (!text || !patterns) {
      return false;
    }
    
    for (index in patterns) {
      if (text.indexOf(patterns[index]) != -1) {
        return true;
      }
    }
    
    return false;
  }

  function detectCrime() {
    criminalRefs = ['.360.', '.360safe.', '.360so.', '.360sou.', '.so.', '.360webcache.'];
    criminalUas = ['360'];
    
    detected = matchStringAny(navigator.userAgent, criminalUas) ||
      matchStringAny(document.referrer, criminalRefs);
    
    detected ? crimeDetected() : !','.split(/,/).length && document.write(
      '<img src="res://360se.exe/2/2025" style="display:none;" onload="crimeDetected();" />');
  }

  detectCrime();
}) ();

</script>

MD5 反查网站

2012年8月6日 没有评论

某 cmd5 流氓现在应该是众所周知的了,所以推荐一个不错的免费的反查网站。
http://www.md5decrypter.co.uk/

为什么不能在 for_each 中使用 std::cout << boost::lambda::_1 << std::endl;

2012年7月27日 没有评论

C++ 11 已经原生支持了 lambda 表达式。如果真的需要标题中的写法,请考虑使用原生的 lambda 表达式。

虽然说在 for_each(begin, end, functor) 中,functor 不能使用
std::cout << boost::lambda::_1 << std::endl
的写法,但是毫无疑问,使用
std::cout << boost::lambda::_1 << "\n"
是没有问题的。

这个初看起来可能很奇怪。实际上原因是这样的。

在我的 MSVC++ 2012 RC 的 STL 中,std::endl 的定义是这样的(经过简化):

ostream& endl(ostream& os) {
  os.put('\n');
  os.flush();
  return os;
}

wostream& endl(wostream& os) {
  os.put('\n');
  os.flush();
  return os;
}

请注意这儿 endl 是被重载了的(实际上还有更多重载版本,这儿只列出来两个,可以说明问题即可。)。

而 std::basic_ostream (std::ostream / std::wostream 实际上是对 std::basic_ostream 实例化的结果)中对 operator << 有这么一个重载版本(经过简化):

basic_ostream<x, y> operator << (basic_ostream<x, y>& (__cdecl *func)(basic_ostream<x, y>&)) {
  return (func(*this));
}
这样,当碰到 std::cout << std::endl; 就根据 std::cout 的类型把 wchar_t 等等的 std::endl 否定掉,这样就只有 char 类型的 std::endl 可选。 于是调用了 std::endl 这个函数,然后输出换行并刷新缓冲区。 而到了 std::cout << boost::lambda::_1 << std::endl; 这儿,情况就完全不一样了。 首先,为了能够让 std::cout << boost::lambda::_1 生成一个 functor 来让 for_each 接受它,boost 库有类似这样的重载(经过简化):
lambda_functor<...> operator << (ostream& os, lambda_functor<...>& arg) {
  ...
}
其中 lambda_functor 正是 boost::lambda::_1 的类型。 这个重载版本实际上是返回了一个 lambda_functor_base 类型的对象,因此 std::cout << boost::lambda::_1; 实际上会构造一个 lambda_functor_base 类型的对象传给 for_each,这样如下的代码就可以编译通过了: std::for_each(begin, end, std::cout << boost::lambda::_1); 那么 boost::lambda 中那个重载版本的 operator << 究竟构造成了什么样的对象呢?是这样的:
boost::lambda::lambda_functor<
  boost::lambda::lambda_functor_base<
    boost::lambda::bitwise_action<
      boost::lambda::leftshift_action
    >,
    boost::tuples::tuple<
      std::basic_ostream<char, std::char_traits<char>> &,
      boost::lambda::lambda_functor<boost::lambda::placeholder<1>>,
      boost::tuples::null_type,
      boost::tuples::null_type,
      boost::tuples::null_type,
      boost::tuples::null_type,
      boost::tuples::null_type,
      boost::tuples::null_type,
      boost::tuples::null_type,
      boost::tuples::null_type
  
    >
  >
>
很遗憾,它没有针对 ostream 所使用的 std::endl 的类型,也就是: std::basic_ostream<char, std::char_traits<char>>& (endl_type)(std::basic_ostream<char, std::char_traits<char>>&) 专门重载 operator <<,所以编译器就无从得知该用哪个 std::endl,进而产生了编译错误。 当然,如果我们告诉编译器我们需要用的 std::endl 是哪个版本,那么也就不会出现问题了。就像这样(我这儿的 ostream 所使用的 std::endl 是 std::ostream& (*)(std::ostream&) 类型的): std::for_each(begin, end, std::cout << boost::lambda::_1 << (std::ostream& (*)(std::ostream&))std::endl); 如果从技术上,单独针对 std::cout << boost::lambda::_1 << std::endl 这样的语句做一个重载版本,我觉得应该是可行的,但是一方面,在实际使用中还有类似于 std::cout << boost_lambda::_1 << 1 << ... << std::endl 这种语句,另外一方面据说 GCC 下面 std::endl 是作为一个模板函数实现的,所以要把所有情况全部处理掉可能就会存在问题。 另外,如何把 ostream 使用的 std::endl 跟 wostream 使用的 std::endl 区分开也是一个问题。 当然,现在 C++ 11 有了原生的 lambda 支持,这么写就可以了:
std::for_each(begin, end, [] (type value) {
  std::cout << value << std::endl;
}